diff --git a/.agents/skills/debug-openshell-cluster/SKILL.md b/.agents/skills/debug-openshell-cluster/SKILL.md index cc77d771b2..45e95234b1 100644 --- a/.agents/skills/debug-openshell-cluster/SKILL.md +++ b/.agents/skills/debug-openshell-cluster/SKILL.md @@ -464,6 +464,7 @@ openshell logs | Supervisor enters policy quarantine | A runtime candidate failed validation while `policy_validation_failure_mode = "fail_closed"` | Sandbox OCSF config/finding events, validation rationale, active generation, `previous_policy_active` | | HTTP request returns `middleware_failed` or `middleware_denied` | Selected stage failed or explicitly denied the admitted request | Sandbox OCSF logs; policy-local middleware config; service availability; `on_error` | | Custom compute driver is unavailable | Driver process/socket missing, inaccessible, or configured with a reserved/mismatched name | Socket ownership/mode, driver service logs, gateway `GetCapabilities` logs | +| Sandbox remains `Stopping` or `Starting` | Driver stop/start failed, retained resource is missing, or a fresh supervisor has not connected | Gateway and driver logs; `docker inspect`, `podman inspect`, Agent Sandbox status/PVC, or VM state marker and launcher process | | Image pull failure | Gateway or sandbox image cannot be pulled | Runtime events and image pull credentials | | `K8s namespace not ready` with `envoy-gateway-openshell.yaml: the server could not find the requested resource` | Optional Gateway API manifest was applied without Envoy Gateway CRDs, or k3s Helm controller startup exceeded the namespace wait | Apply `deploy/kube/manifests/envoy-gateway-openshell.yaml` manually only after Envoy Gateway is installed and `grpcRoute` is enabled | | HTTPS ingress (`grpcRoute.gateway.listener.protocol=HTTPS`) connection resets or TLS handshake hangs | Envoy terminates TLS but the gateway pod still expects TLS, so the plaintext backend hop fails | Set `server.disableTls=true` so Envoy forwards plaintext to the pod; verify the listener `certificateRefs` Secret exists in the release namespace and `openshell status` over `https://` | diff --git a/.agents/skills/helm-dev-environment/SKILL.md b/.agents/skills/helm-dev-environment/SKILL.md index adb40a9575..06f0f5e0d0 100644 --- a/.agents/skills/helm-dev-environment/SKILL.md +++ b/.agents/skills/helm-dev-environment/SKILL.md @@ -220,7 +220,7 @@ ServiceAccount bootstrap and gateway-minted sandbox JWT path. --- -## Cluster Lifecycle (suspend/resume) +## Cluster Lifecycle (stop/start) Stop the cluster without losing state (faster than delete/recreate): ```bash diff --git a/.agents/skills/openshell-cli/SKILL.md b/.agents/skills/openshell-cli/SKILL.md index 462e27f3f2..b49b82c5ec 100644 --- a/.agents/skills/openshell-cli/SKILL.md +++ b/.agents/skills/openshell-cli/SKILL.md @@ -298,6 +298,21 @@ openshell sandbox delete sandbox-1 sandbox-2 sandbox-3 # Multiple at once openshell sandbox delete --all ``` +### Stop and start sandboxes + +Use stop to halt compute while retaining the sandbox and its persistent +workspace: + +```bash +openshell sandbox stop [name] +openshell sandbox start [name] +``` + +Both commands default to the last-used sandbox. Stop stops background +forwards and waits for `Stopped`; start waits for `Ready`. Connect, exec, +file transfer, forwarding, and exposed services are unavailable while +stopped. Delete remains the operation that removes retained state. + --- ## Workflow 4: Policy Iteration Loop @@ -669,7 +684,7 @@ The CLI help is always authoritative. If the help output contradicts this skill, ```bash $ openshell sandbox --help -# Shows: create, get, list, delete, exec, connect, upload, download, ssh-config, provider +# Shows: create, get, list, stop, start, delete, exec, connect, upload, download, ssh-config, provider $ openshell sandbox upload --help # Shows: positional arguments (name, path, dest), usage examples @@ -691,6 +706,8 @@ $ openshell sandbox upload --help | Create sandbox with GPUs | `openshell sandbox create --gpu 1` | | Create with custom policy | `openshell sandbox create --policy ./p.yaml` | | Connect to sandbox | `openshell sandbox connect ` | +| Stop sandbox compute | `openshell sandbox stop [name]` | +| Start sandbox compute | `openshell sandbox start [name]` | | Execute in sandbox | `openshell sandbox exec --name -- ` | | Stream live logs | `openshell logs --tail` | | Incremental policy update | `openshell policy update --add-endpoint host:443:read-only:rest:enforce --binary /usr/bin/curl --wait` | diff --git a/.agents/skills/openshell-cli/cli-reference.md b/.agents/skills/openshell-cli/cli-reference.md index 30d6fb7ed3..ec529508de 100644 --- a/.agents/skills/openshell-cli/cli-reference.md +++ b/.agents/skills/openshell-cli/cli-reference.md @@ -48,6 +48,8 @@ openshell │ ├── create [opts] [-- CMD...] │ ├── get [name] │ ├── list [opts] +│ ├── stop [name] +│ ├── start [name] │ ├── delete [name]... [--all] │ ├── exec [--name ] [opts] -- CMD... │ ├── connect [name] [--editor ] @@ -250,6 +252,17 @@ Show sandbox details and the active policy. Metadata identifies sandbox or globa Delete one or more named sandboxes, or use `--all`. Deletion stops background port forwards. +### `openshell sandbox stop [name]` + +Stop sandbox compute while retaining the sandbox and persistent workspace. The +name defaults to the last-used sandbox. The command stops background forwards +and waits for the `Stopped` phase. + +### `openshell sandbox start [name]` + +Start a stopped sandbox and wait for `Ready`. The name defaults to the +last-used sandbox. + ### `openshell sandbox exec [OPTIONS] -- COMMAND...` Execute a command through the gRPC exec endpoint, stream its output, and exit with the remote command's exit code. diff --git a/TESTING.md b/TESTING.md index e4008143ec..6c0829060d 100644 --- a/TESTING.md +++ b/TESTING.md @@ -148,7 +148,7 @@ lifecycle management, output parsing, and cleanup. Suites: - Common suite (`--features e2e`) - driver-neutral CLI behavior, sandbox lifecycle, sync, port forwarding, policy, and provider tests. -- Docker suite (`--features e2e-docker`) - common suite plus Docker-only coverage such as Dockerfile image builds, Docker preflight checks, and managed Docker gateway resume. +- Docker suite (`--features e2e-docker`) - common suite plus Docker-only coverage such as Dockerfile image builds, Docker preflight checks, and managed Docker gateway start. - Docker GPU suite (`--features e2e-docker-gpu`) - Docker suite plus GPU sandbox smoke coverage. - VM suite (`--features e2e-vm`) - runs e2e tests on a VM. - Kubernetes credential-driver suite (`--features e2e-kubernetes-credential-drivers`) - targeted Kubernetes Secrets and Vault provider credential storage coverage. diff --git a/architecture/compute-runtimes.md b/architecture/compute-runtimes.md index 646b6320bd..e4224232dd 100644 --- a/architecture/compute-runtimes.md +++ b/architecture/compute-runtimes.md @@ -1,6 +1,6 @@ # Compute Runtimes -Compute runtimes create, stop, delete, and watch sandbox workloads for the +Compute runtimes create, stop, start, delete, and watch sandbox workloads for the gateway. They do not replace sandbox policy enforcement. Every runtime starts a workload that runs the `openshell-sandbox` supervisor, and the supervisor enforces the sandbox contract locally. @@ -84,19 +84,42 @@ The gateway records driver identity and version from the startup capability response. Elevated gateway info reports that initialized driver snapshot instead of re-querying drivers on each request. +## Stop and Start Lifecycle + +The gateway persists lifecycle intent before mutating compute: + +```text +Ready -> Stopping -> Stopped -> Starting -> Ready +``` + +`StopSandbox` and `StartSandbox` are idempotent driver operations. Stop +retains the driver resource and its persistent workspace boundary while making +exec, SSH, forwarding, and exposed services unavailable. Start reactivates the +same resource. The gateway requires a fresh supervisor session before a +starting sandbox returns to `Ready`; stale driver snapshots and supervisor +sessions cannot promote a `Stopped` row. + +Persisted `Stopping` and `Starting` rows are retried at startup. Stable +`Stopped` rows remain stopped. Docker and Podman retain the stopped container +and attached storage, Kubernetes retains the Sandbox CR and PVC while scaling +compute to zero, and VM retains its launch request and writable overlay beside +a stop marker. Delete remains a separate operation that removes these +resources. + ## Deletion Lifecycle -Delete requests use per-sandbox gates to serialize delete attempts. A request +Lifecycle requests use per-sandbox gates to serialize stop, start, and +delete attempts. A delete request resolves the name once and remains bound to that stable ID. The only -combined lock order is delete gate, then the gateway-wide state guard; external +combined lock order is lifecycle gate, then the gateway-wide state guard; external driver calls run without the global guard. -Delete gates are process-local and do not coordinate gateway replicas. They +Lifecycle gates are process-local and do not coordinate gateway replicas. They serialize attempts rather than share results: if one attempt fails and recovery restores a deletable state, a request waiting on the gate may retry the driver. Persisted resource-version checks remain the cross-replica safety boundary. -Watcher events do not acquire delete gates. Exact resource-version checks allow +Watcher events do not acquire lifecycle gates. Exact resource-version checks allow them to interleave safely: status snapshots are no-ops for `Deleting` rows, deleted events are idempotent, and snapshots for absent rows are ignored. @@ -109,7 +132,7 @@ sessions, indexes, and watch/log buses are cleaned after confirmed removal. The request acquires both locks before starting owned work, so cancellation while queued does not leave a delete armed. After that commitment point, the owned task prevents cancellation from stranding a mutation. A gateway restart -does not resume a persisted `Deleting` operation. If the backend completed the +does not start a persisted `Deleting` operation. If the backend completed the delete, reconciliation removes the row; otherwise it can remain `Deleting`. ## Runtime Summary diff --git a/crates/openshell-cli/src/commands/common.rs b/crates/openshell-cli/src/commands/common.rs index 65c677eb3f..7b33622f1e 100644 --- a/crates/openshell-cli/src/commands/common.rs +++ b/crates/openshell-cli/src/commands/common.rs @@ -62,6 +62,9 @@ pub fn phase_name(phase: i32) -> &'static str { Ok(SandboxPhase::Ready) => "Ready", Ok(SandboxPhase::Error) => "Error", Ok(SandboxPhase::Deleting) => "Deleting", + Ok(SandboxPhase::Stopping) => "Stopping", + Ok(SandboxPhase::Stopped) => "Stopped", + Ok(SandboxPhase::Starting) => "Starting", Ok(SandboxPhase::Unknown) | Err(_) => "Unknown", } } diff --git a/crates/openshell-cli/src/main.rs b/crates/openshell-cli/src/main.rs index 6733d2d293..7cefd3669a 100644 --- a/crates/openshell-cli/src/main.rs +++ b/crates/openshell-cli/src/main.rs @@ -1524,6 +1524,22 @@ enum SandboxCommands { all: bool, }, + /// Stop a sandbox while preserving its workspace. + #[command(help_template = LEAF_HELP_TEMPLATE, next_help_heading = "FLAGS")] + Stop { + /// Sandbox name (defaults to last-used sandbox). + #[arg(add = ArgValueCompleter::new(completers::complete_sandbox_names))] + name: Option, + }, + + /// Start a stopped sandbox. + #[command(help_template = LEAF_HELP_TEMPLATE, next_help_heading = "FLAGS")] + Start { + /// Sandbox name (defaults to last-used sandbox). + #[arg(add = ArgValueCompleter::new(completers::complete_sandbox_names))] + name: Option, + }, + /// Execute a command in a running sandbox. /// /// Runs a command inside an existing sandbox using the gRPC exec endpoint. @@ -3163,6 +3179,14 @@ async fn run_async() -> Result<()> { ) .await?; } + SandboxCommands::Stop { name } => { + let name = resolve_sandbox_name(name, &ctx.name, &cli.workspace)?; + run::sandbox_stop(endpoint, &name, &cli.workspace, &tls).await?; + } + SandboxCommands::Start { name } => { + let name = resolve_sandbox_name(name, &ctx.name, &cli.workspace)?; + run::sandbox_start(endpoint, &name, &cli.workspace, &tls).await?; + } SandboxCommands::Connect { name, editor } => { let name = resolve_sandbox_name(name, &ctx.name, &cli.workspace)?; if let Some(editor) = editor.map(Into::into) { @@ -4463,6 +4487,27 @@ mod tests { )); } + #[test] + fn sandbox_stop_and_start_accept_optional_names() { + let stop = Cli::try_parse_from(["openshell", "sandbox", "stop", "demo"]) + .expect("stop command should parse"); + assert!(matches!( + stop.command, + Some(Commands::Sandbox { + command: Some(SandboxCommands::Stop { name: Some(ref name) }), + }) if name == "demo" + )); + + let start = Cli::try_parse_from(["openshell", "sandbox", "start"]) + .expect("start command should parse"); + assert!(matches!( + start.command, + Some(Commands::Sandbox { + command: Some(SandboxCommands::Start { name: None }), + }) + )); + } + #[test] fn sandbox_list_accepts_output_json() { let cli = Cli::try_parse_from(["openshell", "sandbox", "list", "-o", "json"]) diff --git a/crates/openshell-cli/src/run.rs b/crates/openshell-cli/src/run.rs index 01d28163d0..e376d08dc5 100644 --- a/crates/openshell-cli/src/run.rs +++ b/crates/openshell-cli/src/run.rs @@ -51,9 +51,9 @@ use openshell_core::proto::{ ProviderProfileImportItem, RejectDraftChunkRequest, ResourceRequirements, RevokeSshSessionRequest, RotateProviderCredentialRequest, Sandbox, SandboxPhase, SandboxPolicy, SandboxSpec, SandboxTemplate, ServiceEndpointResponse, SetInferenceRouteRequest, SettingScope, - TcpForwardFrame, TcpForwardInit, TcpRelayTarget, UpdateConfigRequest, - UpdateProviderProfilesRequest, UpdateProviderRequest, WatchSandboxRequest, exec_sandbox_event, - setting_value, tcp_forward_init, + StartSandboxRequest, StopSandboxRequest, TcpForwardFrame, TcpForwardInit, TcpRelayTarget, + UpdateConfigRequest, UpdateProviderProfilesRequest, UpdateProviderRequest, WatchSandboxRequest, + exec_sandbox_event, setting_value, tcp_forward_init, }; use openshell_core::settings; use openshell_core::{ObjectId, ObjectName, ObjectWorkspace}; @@ -2418,6 +2418,135 @@ pub async fn sandbox_delete( Ok(()) } +/// Stop a sandbox while retaining its persistent workspace. +pub async fn sandbox_stop( + server: &str, + name: &str, + workspace: &str, + tls: &TlsOptions, +) -> Result<()> { + if let Ok(stopped) = stop_forwards_for_sandbox(name) { + for port in stopped { + eprintln!( + "{} Stopped forward of port {port} for sandbox {name}", + "✓".green().bold(), + ); + } + } + + let mut client = grpc_client(server, tls).await?; + let sandbox = client + .stop_sandbox(StopSandboxRequest { + name: name.to_string(), + workspace: workspace.to_string(), + }) + .await + .into_diagnostic()? + .into_inner() + .sandbox + .ok_or_else(|| miette!("gateway returned no sandbox after stop"))?; + wait_for_lifecycle_phase(&mut client, sandbox, SandboxPhase::Stopped).await?; + println!("{} Stopped sandbox {name}", "✓".green().bold()); + Ok(()) +} + +/// Start a stopped sandbox and wait until it is ready. +pub async fn sandbox_start( + server: &str, + name: &str, + workspace: &str, + tls: &TlsOptions, +) -> Result<()> { + let mut client = grpc_client(server, tls).await?; + let sandbox = client + .start_sandbox(StartSandboxRequest { + name: name.to_string(), + workspace: workspace.to_string(), + }) + .await + .into_diagnostic()? + .into_inner() + .sandbox + .ok_or_else(|| miette!("gateway returned no sandbox after start"))?; + wait_for_lifecycle_phase(&mut client, sandbox, SandboxPhase::Ready).await?; + println!("{} Started sandbox {name}", "✓".green().bold()); + Ok(()) +} + +async fn wait_for_lifecycle_phase( + client: &mut crate::tls::GrpcClient, + sandbox: Sandbox, + target: SandboxPhase, +) -> Result { + let current = SandboxPhase::try_from(sandbox.phase()).unwrap_or(SandboxPhase::Unknown); + if current == target { + return Ok(sandbox); + } + if current == SandboxPhase::Error { + return Err(miette!( + "sandbox entered Error while waiting for {target:?}" + )); + } + + let timeout = Duration::from_secs( + std::env::var("OPENSHELL_LIFECYCLE_TIMEOUT") + .ok() + .and_then(|value| value.parse().ok()) + .unwrap_or(300), + ); + let sandbox_id = sandbox.object_id().to_string(); + let mut stream = client + .watch_sandbox(WatchSandboxRequest { + id: sandbox_id, + follow_status: true, + follow_logs: false, + follow_events: false, + log_tail_lines: 0, + event_tail: 0, + stop_on_terminal: false, + log_since_ms: 0, + log_sources: Vec::new(), + log_min_level: String::new(), + }) + .await + .into_diagnostic()? + .into_inner(); + + let deadline = Instant::now() + timeout; + loop { + let remaining = deadline.saturating_duration_since(Instant::now()); + if remaining.is_zero() { + return Err(miette!( + "timed out after {}s waiting for sandbox to reach {target:?}", + timeout.as_secs() + )); + } + let event = tokio::time::timeout(remaining, stream.next()) + .await + .map_err(|_| { + miette!( + "timed out after {}s waiting for sandbox to reach {target:?}", + timeout.as_secs() + ) + })? + .ok_or_else(|| miette!("sandbox watch ended before reaching {target:?}"))? + .into_diagnostic()?; + if let Some(openshell_core::proto::sandbox_stream_event::Payload::Sandbox(sandbox)) = + event.payload + { + let phase = SandboxPhase::try_from(sandbox.phase()).unwrap_or(SandboxPhase::Unknown); + if phase == target { + return Ok(sandbox); + } + if phase == SandboxPhase::Error { + let detail = ready_false_condition_message(sandbox.status.as_ref()) + .unwrap_or_else(|| "sandbox entered Error".to_string()); + return Err(miette!(detail)); + } + } + } +} + /// Return the provider type inferred from the trailing command, if any. fn inferred_provider_type(command: &[String]) -> Option { detect_provider_from_command(command).map(str::to_string) diff --git a/crates/openshell-cli/tests/ensure_providers_integration.rs b/crates/openshell-cli/tests/ensure_providers_integration.rs index 5bd64c2f36..3d628f2c10 100644 --- a/crates/openshell-cli/tests/ensure_providers_integration.rs +++ b/crates/openshell-cli/tests/ensure_providers_integration.rs @@ -111,6 +111,20 @@ impl OpenShell for TestOpenShell { Ok(Response::new(SandboxResponse::default())) } + async fn stop_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + + async fn start_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + async fn get_sandbox( &self, _request: tonic::Request, diff --git a/crates/openshell-cli/tests/mtls_integration.rs b/crates/openshell-cli/tests/mtls_integration.rs index 38c68ed83a..60ffbd61f8 100644 --- a/crates/openshell-cli/tests/mtls_integration.rs +++ b/crates/openshell-cli/tests/mtls_integration.rs @@ -66,6 +66,20 @@ impl OpenShell for TestOpenShell { )) } + async fn stop_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + + async fn start_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + async fn get_sandbox( &self, _request: tonic::Request, diff --git a/crates/openshell-cli/tests/provider_commands_integration.rs b/crates/openshell-cli/tests/provider_commands_integration.rs index 24645ea259..a87ff0a6d8 100644 --- a/crates/openshell-cli/tests/provider_commands_integration.rs +++ b/crates/openshell-cli/tests/provider_commands_integration.rs @@ -129,6 +129,20 @@ impl OpenShell for TestOpenShell { Ok(Response::new(SandboxResponse::default())) } + async fn stop_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + + async fn start_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + async fn get_sandbox( &self, request: tonic::Request, diff --git a/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs b/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs index 43e72f64bb..102cde3714 100644 --- a/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs +++ b/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs @@ -116,6 +116,20 @@ impl OpenShell for TestOpenShell { })) } + async fn stop_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + + async fn start_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + async fn get_sandbox( &self, request: tonic::Request, diff --git a/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs b/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs index 019b2b12e4..41b93bab82 100644 --- a/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs +++ b/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs @@ -79,6 +79,20 @@ impl OpenShell for TestOpenShell { Ok(Response::new(SandboxResponse::default())) } + async fn stop_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + + async fn start_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + async fn get_sandbox( &self, request: tonic::Request, diff --git a/crates/openshell-core/src/error.rs b/crates/openshell-core/src/error.rs index a149cf006a..8c23e30198 100644 --- a/crates/openshell-core/src/error.rs +++ b/crates/openshell-core/src/error.rs @@ -113,6 +113,9 @@ pub enum ComputeDriverError { /// The requested sandbox already exists. #[error("sandbox already exists")] AlreadyExists, + /// The requested sandbox does not exist. + #[error("sandbox not found")] + NotFound, /// The request contains an invalid argument. #[error("{0}")] InvalidArgument(String), @@ -128,6 +131,7 @@ impl From for tonic::Status { fn from(err: ComputeDriverError) -> Self { match err { ComputeDriverError::AlreadyExists => Self::already_exists("sandbox already exists"), + ComputeDriverError::NotFound => Self::not_found("sandbox not found"), ComputeDriverError::InvalidArgument(m) => Self::invalid_argument(m), ComputeDriverError::Precondition(m) => Self::failed_precondition(m), ComputeDriverError::Message(m) => Self::internal(m), diff --git a/crates/openshell-core/src/telemetry.rs b/crates/openshell-core/src/telemetry.rs index 49ce620f4f..b2c9b79152 100644 --- a/crates/openshell-core/src/telemetry.rs +++ b/crates/openshell-core/src/telemetry.rs @@ -103,6 +103,8 @@ impl LifecycleResource { pub enum LifecycleOperation { Create, Delete, + Stop, + Start, Update, } @@ -112,6 +114,8 @@ impl LifecycleOperation { match self { Self::Create => "create", Self::Delete => "delete", + Self::Stop => "stop", + Self::Start => "start", Self::Update => "update", } } diff --git a/crates/openshell-driver-docker/README.md b/crates/openshell-driver-docker/README.md index 05faf53c5c..6f364a151c 100644 --- a/crates/openshell-driver-docker/README.md +++ b/crates/openshell-driver-docker/README.md @@ -18,6 +18,15 @@ The gateway runs as a host process. The Docker driver creates one container per sandbox and starts the `openshell-sandbox` supervisor inside that container. The supervisor then creates the nested sandbox namespace for the agent process. +## Stop and Start + +Stop stops the managed container without removing it. Docker retains the +container writable layer, attached volumes, labels, token material, and restart +policy. Start starts that same container, so files in the resolved OCI +workspace remain available. A durably stopped sandbox is excluded from +gateway startup recovery and stays stopped across gateway restarts. Delete +continues to force-remove the container and clean up driver-owned material. + Before creating the container, the driver inspects the final sandbox image and captures its immutable image ID, raw OCI `Config.User`, and OCI `Config.WorkingDir`. Container creation uses that image ID, preventing a diff --git a/crates/openshell-driver-docker/src/lib.rs b/crates/openshell-driver-docker/src/lib.rs index dd4d9ef0f0..7881f4c8d7 100644 --- a/crates/openshell-driver-docker/src/lib.rs +++ b/crates/openshell-driver-docker/src/lib.rs @@ -8,10 +8,10 @@ use bollard::Docker; use bollard::errors::Error as BollardError; use bollard::models::{ - ContainerCreateBody, ContainerSummary, ContainerSummaryStateEnum, CreateImageInfo, - DeviceRequest, EndpointSettings, HostConfig, Mount, MountTmpfsOptions, MountTypeEnum, - MountVolumeOptions, NetworkCreateRequest, NetworkingConfig, ProgressDetail, RestartPolicy, - RestartPolicyNameEnum, SystemInfo, + ContainerCreateBody, ContainerState, ContainerStateStatusEnum, ContainerSummary, + ContainerSummaryStateEnum, CreateImageInfo, DeviceRequest, EndpointSettings, HostConfig, Mount, + MountTmpfsOptions, MountTypeEnum, MountVolumeOptions, NetworkCreateRequest, NetworkingConfig, + ProgressDetail, RestartPolicy, RestartPolicyNameEnum, SystemInfo, }; use bollard::query_parameters::{ CreateContainerOptionsBuilder, CreateImageOptions, DownloadFromContainerOptionsBuilder, @@ -42,11 +42,12 @@ use openshell_core::proto::compute::v1::{ DriverSandboxTemplate, GatewayListenerRequirement, GetCapabilitiesRequest, GetCapabilitiesResponse, GetGatewayListenerRequirementsRequest, GetGatewayListenerRequirementsResponse, GetSandboxRequest, GetSandboxResponse, - GpuResourceRequirements, ListSandboxesRequest, ListSandboxesResponse, StopSandboxRequest, - StopSandboxResponse, ValidateSandboxCreateRequest, ValidateSandboxCreateResponse, - WatchSandboxesDeletedEvent, WatchSandboxesEvent, WatchSandboxesPlatformEvent, - WatchSandboxesRequest, WatchSandboxesSandboxEvent, compute_driver_server::ComputeDriver, - gateway_listener_requirement::Selector, watch_sandboxes_event, + GpuResourceRequirements, ListSandboxesRequest, ListSandboxesResponse, StartSandboxRequest, + StartSandboxResponse, StopSandboxRequest, StopSandboxResponse, ValidateSandboxCreateRequest, + ValidateSandboxCreateResponse, WatchSandboxesDeletedEvent, WatchSandboxesEvent, + WatchSandboxesPlatformEvent, WatchSandboxesRequest, WatchSandboxesSandboxEvent, + compute_driver_server::ComputeDriver, gateway_listener_requirement::Selector, + watch_sandboxes_event, }; use openshell_core::proto_struct::{ deserialize_optional_non_empty_string_list, struct_to_json_value, @@ -63,7 +64,7 @@ use tokio::sync::{Mutex, broadcast, mpsc}; use tokio::task::JoinHandle; use tokio_stream::wrappers::ReceiverStream; use tonic::{Request, Response, Status}; -use tracing::{info, warn}; +use tracing::{debug, info, warn}; use url::Url; const WATCH_BUFFER: usize = 128; @@ -204,6 +205,85 @@ pub struct DockerComputeDriver { events: broadcast::Sender, pending: Arc>>, gpu_selector: Arc, + lifecycle_event_fences: DockerLifecycleEventFences, +} + +/// Per-sandbox container exit timestamps that fence snapshots from an earlier run. +/// +/// Docker's polling loop can observe the stopped container before a restart and +/// publish that snapshot after the gateway has moved the sandbox to `Starting`. +/// Comparing the container's transition timestamp prevents that old observation +/// from regressing the new lifecycle operation to `Error`. +#[derive(Clone, Debug, Default)] +struct DockerLifecycleEventFences { + state: Arc>, +} + +#[derive(Debug, Default)] +struct DockerLifecycleFenceState { + previous_finished_at: HashMap, + starts_in_progress: HashSet, +} + +impl DockerLifecycleEventFences { + fn begin_start(&self, sandbox_id: &str) { + self.state + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .starts_in_progress + .insert(sandbox_id.to_string()); + } + + fn finish_start(&self, sandbox_id: &str) { + self.state + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .starts_in_progress + .remove(sandbox_id); + } + + fn start_in_progress(&self, sandbox_id: &str) -> bool { + self.state + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .starts_in_progress + .contains(sandbox_id) + } + + fn record_previous_exit(&self, sandbox_id: &str, finished_at: Option<&str>) { + let mut state = self + .state + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + match finished_at.filter(|finished_at| !finished_at.is_empty()) { + Some(finished_at) => { + state + .previous_finished_at + .insert(sandbox_id.to_string(), finished_at.to_string()); + } + None => { + state.previous_finished_at.remove(sandbox_id); + } + } + } + + fn previous_exit(&self, sandbox_id: &str) -> Option { + self.state + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .previous_finished_at + .get(sandbox_id) + .cloned() + } + + fn remove(&self, sandbox_id: &str) { + let mut state = self + .state + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + state.previous_finished_at.remove(sandbox_id); + state.starts_in_progress.remove(sandbox_id); + } } struct PendingSandboxRecord { @@ -393,6 +473,7 @@ impl DockerComputeDriver { cdi_gpu_inventory, allow_all_default_gpu, )), + lifecycle_event_fences: DockerLifecycleEventFences::default(), }; let poll_driver = driver.clone(); @@ -900,14 +981,27 @@ impl DockerComputeDriver { } /// Start a managed sandbox container that was previously stopped. Used - /// by the gateway to resume sandboxes after a restart so that running + /// by the gateway to start sandboxes after a restart so that running /// state in the gateway store is matched by an actually-running /// container. /// /// Returns `Ok(true)` when a container existed and was started (or was /// already running), `Ok(false)` when no managed container is found for /// the sandbox, and `Err(...)` for any Docker failure. - pub async fn resume_sandbox( + pub async fn start_sandbox( + &self, + sandbox_id: &str, + sandbox_name: &str, + ) -> Result { + self.lifecycle_event_fences.begin_start(sandbox_id); + let result = self + .start_sandbox_with_lifecycle_fence(sandbox_id, sandbox_name) + .await; + self.lifecycle_event_fences.finish_start(sandbox_id); + result + } + + async fn start_sandbox_with_lifecycle_fence( &self, sandbox_id: &str, sandbox_name: &str, @@ -922,13 +1016,33 @@ impl DockerComputeDriver { return Ok(false); }; let state = container.state.unwrap_or(ContainerSummaryStateEnum::EMPTY); - if !container_state_needs_resume(state) { + if !container_state_needs_start(state) { return Ok(true); } + // Fence a poll that observed this stopped run but has not published it + // yet. Use Docker's transition timestamp so a later, genuine exit from + // the restarted container remains observable. + let previous_finished_at = if state == ContainerSummaryStateEnum::EXITED { + let inspected = self + .docker + .inspect_container(&target, None) + .await + .map_err(|err| internal_status("inspect docker sandbox before start", err))?; + inspected + .state + .as_ref() + .filter(|state| state.status == Some(ContainerStateStatusEnum::EXITED)) + .and_then(|state| state.finished_at.clone()) + } else { + None + }; + self.lifecycle_event_fences + .record_previous_exit(sandbox_id, previous_finished_at.as_deref()); + match self.docker.start_container(&target, None).await { Ok(()) => Ok(true), - // Already running — race with another resume path or the + // Already running — race with another start path or the // restart policy. Treat as success. Err(err) if is_not_modified_error(&err) => Ok(true), Err(err) if is_not_found_error(&err) => Ok(false), @@ -1176,7 +1290,7 @@ impl DockerComputeDriver { tokio::time::sleep(backoff).await; match self.current_snapshot_map().await { Ok(current) => { - emit_snapshot_diff(&self.events, &previous, ¤t); + self.publish_snapshot_diff(&previous, ¤t).await; previous = current; backoff = WATCH_POLL_INTERVAL; } @@ -1201,6 +1315,78 @@ impl DockerComputeDriver { }) } + async fn publish_snapshot_diff( + &self, + previous: &HashMap, + current: &HashMap, + ) { + for (sandbox_id, sandbox) in current { + if previous.get(sandbox_id) == Some(sandbox) { + continue; + } + if self.stale_polled_exit(sandbox).await { + continue; + } + self.publish_sandbox_snapshot(sandbox.clone()); + } + + for sandbox_id in previous.keys() { + if current.contains_key(sandbox_id) { + continue; + } + self.publish_deleted(sandbox_id.clone()); + } + } + + async fn stale_polled_exit(&self, sandbox: &DriverSandbox) -> bool { + if !driver_sandbox_reports_container_exit(sandbox) { + return false; + } + if self.lifecycle_event_fences.start_in_progress(&sandbox.id) { + debug!( + sandbox_id = %sandbox.id, + "Ignoring Docker container exit snapshot while sandbox start is in progress" + ); + return true; + } + let Some(previous_finished_at) = self.lifecycle_event_fences.previous_exit(&sandbox.id) + else { + return false; + }; + let Some(container_id) = sandbox + .status + .as_ref() + .map(|status| status.instance_id.as_str()) + .filter(|container_id| !container_id.is_empty()) + else { + return false; + }; + + let inspected = match self.docker.inspect_container(container_id, None).await { + Ok(inspected) => inspected, + Err(err) => { + debug!( + sandbox_id = %sandbox.id, + container_id, + error = %err, + "Could not verify whether polled Docker exit predates sandbox start" + ); + return false; + } + }; + if !docker_polled_exit_is_stale(&previous_finished_at, inspected.state.as_ref()) { + return false; + } + + debug!( + sandbox_id = %sandbox.id, + container_id, + previous_finished_at, + "Ignoring Docker container exit snapshot from before the latest sandbox start" + ); + true + } + async fn list_managed_container_summaries(&self) -> Result, Status> { let filters = managed_container_label_filters(&self.config.sandbox_namespace, []); self.docker @@ -1479,9 +1665,25 @@ impl ComputeDriver for DockerComputeDriver { self.stop_sandbox_inner(&request.sandbox_id, &request.sandbox_name) .await?; + self.publish_container_snapshot(&request.sandbox_id, &request.sandbox_name) + .await?; Ok(Response::new(StopSandboxResponse {})) } + async fn start_sandbox( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + require_sandbox_identifier(&request.sandbox_id, &request.sandbox_name)?; + if !Self::start_sandbox(self, &request.sandbox_id, &request.sandbox_name).await? { + return Err(Status::not_found("sandbox not found")); + } + self.publish_container_snapshot(&request.sandbox_id, &request.sandbox_name) + .await?; + Ok(Response::new(StartSandboxResponse {})) + } + async fn delete_sandbox( &self, request: Request, @@ -1493,6 +1695,7 @@ impl ComputeDriver for DockerComputeDriver { let deleted = self .delete_sandbox_inner(&request.sandbox_id, &request.sandbox_name) .await?; + self.lifecycle_event_fences.remove(&event_sandbox_id); if deleted && !event_sandbox_id.is_empty() { let _ = self.events.send(WatchSandboxesEvent { payload: Some(watch_sandboxes_event::Payload::Deleted( @@ -2966,7 +3169,7 @@ fn container_state_needs_shutdown_stop(state: ContainerSummaryStateEnum) -> bool /// `start_container`. Skip `Restarting` (already coming up), `Removing`, /// `Dead` (terminal), `Paused` (needs `unpause`, not `start`), and /// `Running` (nothing to do). -fn container_state_needs_resume(state: ContainerSummaryStateEnum) -> bool { +fn container_state_needs_start(state: ContainerSummaryStateEnum) -> bool { matches!( state, ContainerSummaryStateEnum::EXITED | ContainerSummaryStateEnum::CREATED @@ -2977,36 +3180,31 @@ fn docker_stop_timeout_secs(timeout_secs: u32) -> i32 { i32::try_from(timeout_secs).unwrap_or(i32::MAX) } -fn emit_snapshot_diff( - events: &broadcast::Sender, - previous: &HashMap, - current: &HashMap, -) { - for (sandbox_id, sandbox) in current { - if previous.get(sandbox_id) == Some(sandbox) { - continue; - } - let _ = events.send(WatchSandboxesEvent { - payload: Some(watch_sandboxes_event::Payload::Sandbox( - WatchSandboxesSandboxEvent { - sandbox: Some(sandbox.clone()), - }, - )), - }); - } +fn driver_sandbox_reports_container_exit(sandbox: &DriverSandbox) -> bool { + sandbox.status.as_ref().is_some_and(|status| { + status.conditions.iter().any(|condition| { + condition.r#type == "Ready" + && condition.status.eq_ignore_ascii_case("false") + && condition.reason == "ContainerExited" + }) + }) +} - for sandbox_id in previous.keys() { - if current.contains_key(sandbox_id) { - continue; - } - let _ = events.send(WatchSandboxesEvent { - payload: Some(watch_sandboxes_event::Payload::Deleted( - WatchSandboxesDeletedEvent { - sandbox_id: sandbox_id.clone(), - }, - )), - }); +fn docker_polled_exit_is_stale( + previous_finished_at: &str, + current_state: Option<&ContainerState>, +) -> bool { + let Some(current_state) = current_state else { + return false; + }; + + if current_state.status != Some(ContainerStateStatusEnum::EXITED) { + // The list response said Exited, but inspect has already observed a + // newer state. Publishing the older list result would regress it. + return true; } + + current_state.finished_at.as_deref() == Some(previous_finished_at) } fn label_filters(values: impl IntoIterator) -> HashMap> { diff --git a/crates/openshell-driver-docker/src/tests.rs b/crates/openshell-driver-docker/src/tests.rs index ac525c705c..13c987235a 100644 --- a/crates/openshell-driver-docker/src/tests.rs +++ b/crates/openshell-driver-docker/src/tests.rs @@ -156,6 +156,7 @@ fn test_driver_with_config(config: DockerDriverRuntimeConfig) -> DockerComputeDr CdiGpuInventory::default(), allow_all_default_gpu, )), + lifecycle_event_fences: DockerLifecycleEventFences::default(), } } @@ -2469,14 +2470,14 @@ fn extract_first_tar_entry_rejects_empty_archive() { } #[test] -fn container_state_needs_resume_matches_startable_states() { +fn container_state_needs_start_matches_startable_states() { for state in [ ContainerSummaryStateEnum::EXITED, ContainerSummaryStateEnum::CREATED, ] { assert!( - container_state_needs_resume(state), - "{state:?} should be resumed with Docker start", + container_state_needs_start(state), + "{state:?} should be started with Docker start", ); } @@ -2489,8 +2490,54 @@ fn container_state_needs_resume_matches_startable_states() { ContainerSummaryStateEnum::EMPTY, ] { assert!( - !container_state_needs_resume(state), - "{state:?} should not be resumed with Docker start", + !container_state_needs_start(state), + "{state:?} should not be started with Docker start", ); } } + +#[test] +fn lifecycle_fence_rejects_polled_exit_from_before_restart() { + let fences = DockerLifecycleEventFences::default(); + fences.begin_start("sandbox-1"); + assert!(fences.start_in_progress("sandbox-1")); + fences.finish_start("sandbox-1"); + assert!(!fences.start_in_progress("sandbox-1")); + + fences.record_previous_exit("sandbox-1", Some("2026-08-12T16:39:13Z")); + assert_eq!( + fences.previous_exit("sandbox-1").as_deref(), + Some("2026-08-12T16:39:13Z") + ); + + let previous_exit = ContainerState { + status: Some(ContainerStateStatusEnum::EXITED), + finished_at: Some("2026-08-12T16:39:13Z".to_string()), + ..Default::default() + }; + assert!(docker_polled_exit_is_stale( + "2026-08-12T16:39:13Z", + Some(&previous_exit), + )); + + let running = ContainerState { + status: Some(ContainerStateStatusEnum::RUNNING), + ..previous_exit.clone() + }; + assert!(docker_polled_exit_is_stale( + "2026-08-12T16:39:13Z", + Some(&running), + )); + + let new_exit = ContainerState { + finished_at: Some("2026-08-12T16:40:00Z".to_string()), + ..previous_exit + }; + assert!(!docker_polled_exit_is_stale( + "2026-08-12T16:39:13Z", + Some(&new_exit), + )); + + fences.remove("sandbox-1"); + assert!(fences.previous_exit("sandbox-1").is_none()); +} diff --git a/crates/openshell-driver-kubernetes/README.md b/crates/openshell-driver-kubernetes/README.md index 1356e2d932..d9c0e17fd8 100644 --- a/crates/openshell-driver-kubernetes/README.md +++ b/crates/openshell-driver-kubernetes/README.md @@ -36,6 +36,15 @@ This is a stopgap persistence model. It preserves user files across pod rescheduling but duplicates the base workspace and does not automatically apply image updates to existing PVCs. Future snapshotting should replace it. +Stop preserves the Agent Sandbox resource and workspace PVC while stopping +its pod. The driver sets `spec.operatingMode: Suspended` for `v1beta1` or +`spec.replicas: 0` for `v1alpha1`. Start sets `Running` or one replica for the +same resource, so the replacement pod mounts the existing claim. Delete is the +only lifecycle operation that removes the Sandbox resource and its owned +storage. The driver confirms the stop from the published `Suspended` +condition when available. Legacy `v1alpha1` controllers omit a zero replica +count from status, so the driver confirms that their backing pod is gone. + The workspace PVC size defaults to `workspace_default_storage_size`. Set `workspace_storage_class` to pin the PVC to a specific `StorageClass`; an empty value omits `storageClassName` so the cluster's default `StorageClass` applies. diff --git a/crates/openshell-driver-kubernetes/src/driver.rs b/crates/openshell-driver-kubernetes/src/driver.rs index 2f1ea72a32..00e38f6dd2 100644 --- a/crates/openshell-driver-kubernetes/src/driver.rs +++ b/crates/openshell-driver-kubernetes/src/driver.rs @@ -11,9 +11,12 @@ use crate::config::{ }; use futures::{Stream, StreamExt, TryStreamExt}; use k8s_openapi::api::core::v1::{ - Event as KubeEventObj, Namespace, Node, PersistentVolumeClaimVolumeSource, Volume, VolumeMount, + Event as KubeEventObj, Namespace, Node, PersistentVolumeClaimVolumeSource, Pod, Volume, + VolumeMount, +}; +use kube::api::{ + Api, ApiResource, DeleteParams, ListParams, Patch, PatchParams, PostParams, Preconditions, }; -use kube::api::{Api, ApiResource, DeleteParams, ListParams, PostParams, Preconditions}; use kube::core::gvk::GroupVersionKind; use kube::core::{DynamicObject, ObjectMeta}; use kube::runtime::watcher::{self, Event}; @@ -87,11 +90,20 @@ impl From for openshell_core::ComputeDriverError { /// API server is unreachable or slow. const KUBE_API_TIMEOUT: Duration = Duration::from_secs(30); +/// Kubernetes defaults pod termination to 30 seconds when the pod template +/// omits `terminationGracePeriodSeconds`. +const DEFAULT_POD_TERMINATION_GRACE_PERIOD: Duration = Duration::from_secs(30); +const STOP_INITIAL_POLL_INTERVAL: Duration = Duration::from_millis(250); +const STOP_MAX_POLL_INTERVAL: Duration = Duration::from_secs(2); + const SANDBOX_GROUP: &str = "agents.x-k8s.io"; const SANDBOX_VERSION_V1BETA1: &str = "v1beta1"; const SANDBOX_VERSION_V1ALPHA1: &str = "v1alpha1"; const SANDBOX_VERSIONS: &[&str] = &[SANDBOX_VERSION_V1BETA1, SANDBOX_VERSION_V1ALPHA1]; pub const SANDBOX_KIND: &str = "Sandbox"; +const SANDBOX_POD_NAME_ANNOTATION: &str = "agents.x-k8s.io/pod-name"; +const SANDBOX_SUSPENDED_CONDITION: &str = "Suspended"; +const SANDBOX_SUSPENDED_POD_NOT_OWNED_REASON: &str = "PodNotOwned"; const GPU_RESOURCE_NAME: &str = "nvidia.com/gpu"; const SPIFFE_WORKLOAD_API_VOLUME_NAME: &str = "spiffe-workload-api"; @@ -927,6 +939,138 @@ impl KubernetesComputeDriver { } } + pub async fn stop_sandbox(&self, sandbox_id: &str) -> Result<(), String> { + let (agent_sandbox_api, kube_name, pod_name, stop_timeout) = self + .patch_sandbox_operating_state(sandbox_id, false) + .await?; + let legacy_pod_api = (agent_sandbox_api.resource.version == SANDBOX_VERSION_V1ALPHA1) + .then(|| Api::::namespaced(self.client.clone(), &self.config.namespace)); + + let deadline = tokio::time::Instant::now() + stop_timeout; + let mut poll_interval = STOP_INITIAL_POLL_INTERVAL; + loop { + let now = tokio::time::Instant::now(); + if now >= deadline { + return Err(format!( + "timed out after {}s waiting for Kubernetes sandbox to stop", + stop_timeout.as_secs() + )); + } + let request_timeout = KUBE_API_TIMEOUT.min(deadline.saturating_duration_since(now)); + let object = tokio::time::timeout( + request_timeout, + agent_sandbox_api.api.get(&kube_name), + ) + .await + .map_err(|_| { + format!( + "timed out after {}s waiting for Kubernetes API while checking sandbox stop", + request_timeout.as_secs() + ) + })? + .map_err(|err| err.to_string())?; + if kubernetes_sandbox_has_stopped_condition(&object) { + return Ok(()); + } + if let Some(error) = kubernetes_sandbox_stop_failure(&object) { + return Err(error); + } + if let Some(pod_api) = legacy_pod_api.as_ref() + && kubernetes_sandbox_pod_is_gone(pod_api, &pod_name, deadline).await? + { + return Ok(()); + } + let now = tokio::time::Instant::now(); + if now >= deadline { + return Err(format!( + "timed out after {}s waiting for Kubernetes sandbox to stop", + stop_timeout.as_secs() + )); + } + tokio::time::sleep(poll_interval.min(deadline.saturating_duration_since(now))).await; + poll_interval = next_stop_poll_interval(poll_interval); + } + } + + pub async fn start_sandbox(&self, sandbox_id: &str) -> Result<(), String> { + self.patch_sandbox_operating_state(sandbox_id, true) + .await + .map(|_| ()) + } + + async fn patch_sandbox_operating_state( + &self, + sandbox_id: &str, + running: bool, + ) -> Result<(AgentSandboxApi, String, String, Duration), String> { + let agent_sandbox_api = self + .supported_agent_sandbox_api(self.client.clone()) + .await?; + let selector = + format!("{LABEL_MANAGED_BY}={LABEL_MANAGED_BY_VALUE},{LABEL_SANDBOX_ID}={sandbox_id}"); + let list = tokio::time::timeout( + KUBE_API_TIMEOUT, + agent_sandbox_api + .api + .list(&ListParams::default().labels(&selector)), + ) + .await + .map_err(|_| { + format!( + "timed out after {}s waiting for Kubernetes API", + KUBE_API_TIMEOUT.as_secs() + ) + })? + .map_err(|err| err.to_string())?; + let object = list + .items + .into_iter() + .next() + .ok_or_else(|| "sandbox not found".to_string())?; + let stop_timeout = kubernetes_sandbox_stop_timeout(&object); + let kube_name = object + .metadata + .name + .ok_or_else(|| "sandbox resource has no name".to_string())?; + let pod_name = object + .metadata + .annotations + .as_ref() + .and_then(|annotations| annotations.get(SANDBOX_POD_NAME_ANNOTATION)) + .cloned() + .unwrap_or_else(|| kube_name.clone()); + let resource_version = object.metadata.resource_version.unwrap_or_default(); + let desired = sandbox_operating_state_patch( + &agent_sandbox_api.resource.version, + &resource_version, + running, + ); + tokio::time::timeout( + KUBE_API_TIMEOUT, + agent_sandbox_api.api.patch( + &kube_name, + &PatchParams::default(), + &Patch::Merge(&desired), + ), + ) + .await + .map_err(|_| { + format!( + "timed out after {}s waiting for Kubernetes API", + KUBE_API_TIMEOUT.as_secs() + ) + })? + .map_err(|err| err.to_string())?; + + info!( + sandbox_id, + sandbox_api_version = %agent_sandbox_api.resource.version, + running, + "Updated Kubernetes sandbox operating state" + ); + Ok((agent_sandbox_api, kube_name, pod_name, stop_timeout)) + } + pub async fn delete_sandbox(&self, sandbox_id: &str) -> Result { info!( sandbox_id = %sandbox_id, @@ -3121,6 +3265,111 @@ fn status_from_object(obj: &DynamicObject) -> Option { }) } +fn kubernetes_sandbox_has_stopped_condition(obj: &DynamicObject) -> bool { + obj.data + .get("status") + .and_then(|status| status.get("conditions")) + .and_then(serde_json::Value::as_array) + .is_some_and(|conditions| { + conditions.iter().any(|condition| { + condition.get("type").and_then(serde_json::Value::as_str) + == Some(SANDBOX_SUSPENDED_CONDITION) + && condition + .get("status") + .and_then(serde_json::Value::as_str) + .is_some_and(|status| status.eq_ignore_ascii_case("true")) + }) + }) +} + +fn kubernetes_sandbox_stop_failure(obj: &DynamicObject) -> Option { + obj.data + .get("status")? + .get("conditions")? + .as_array()? + .iter() + .find_map(|condition| { + let is_terminal = condition.get("type").and_then(serde_json::Value::as_str) + == Some(SANDBOX_SUSPENDED_CONDITION) + && condition + .get("status") + .and_then(serde_json::Value::as_str) + .is_some_and(|status| status.eq_ignore_ascii_case("false")) + && condition.get("reason").and_then(serde_json::Value::as_str) + == Some(SANDBOX_SUSPENDED_POD_NOT_OWNED_REASON); + if !is_terminal { + return None; + } + + let message = condition + .get("message") + .and_then(serde_json::Value::as_str) + .filter(|message| !message.is_empty()) + .unwrap_or("backing pod is not owned by this sandbox"); + Some(format!("Kubernetes sandbox stop rejected: {message}")) + }) +} + +async fn kubernetes_sandbox_pod_is_gone( + pod_api: &Api, + pod_name: &str, + deadline: tokio::time::Instant, +) -> Result { + let request_timeout = + KUBE_API_TIMEOUT.min(deadline.saturating_duration_since(tokio::time::Instant::now())); + if request_timeout.is_zero() { + return Ok(false); + } + + match tokio::time::timeout(request_timeout, pod_api.get(pod_name)).await { + Ok(Ok(_)) => Ok(false), + Ok(Err(KubeError::Api(err))) if err.code == 404 => Ok(true), + Ok(Err(err)) => Err(err.to_string()), + Err(_) => Err(format!( + "timed out after {}s waiting for Kubernetes API while checking sandbox pod termination", + request_timeout.as_secs() + )), + } +} + +fn kubernetes_sandbox_stop_timeout(obj: &DynamicObject) -> Duration { + let termination_grace_period = obj + .data + .get("spec") + .and_then(|spec| spec.get("podTemplate")) + .and_then(|template| template.get("spec")) + .and_then(|spec| spec.get("terminationGracePeriodSeconds")) + .and_then(serde_json::Value::as_u64) + .map_or(DEFAULT_POD_TERMINATION_GRACE_PERIOD, Duration::from_secs); + + // The controller must observe the desired state, wait for the pod grace + // period and kubelet teardown, then reconcile the deleted pod into the + // Sandbox status. Keep one API timeout of headroom around that grace. + termination_grace_period.saturating_add(KUBE_API_TIMEOUT) +} + +fn next_stop_poll_interval(current: Duration) -> Duration { + current.saturating_mul(2).min(STOP_MAX_POLL_INTERVAL) +} + +fn sandbox_operating_state_patch( + api_version: &str, + resource_version: &str, + running: bool, +) -> serde_json::Value { + if api_version == SANDBOX_VERSION_V1BETA1 { + serde_json::json!({ + "metadata": {"resourceVersion": resource_version}, + "spec": {"operatingMode": if running { "Running" } else { "Suspended" }} + }) + } else { + serde_json::json!({ + "metadata": {"resourceVersion": resource_version}, + "spec": {"replicas": i32::from(running)} + }) + } +} + fn condition_from_value(value: &serde_json::Value) -> Option { let obj = value.as_object()?; Some(SandboxCondition { @@ -3194,6 +3443,120 @@ mod tests { assert!(should_try_next_sandbox_api_version(&raw)); } + #[test] + fn lifecycle_patch_uses_version_specific_operating_state() { + let beta_stop = sandbox_operating_state_patch(SANDBOX_VERSION_V1BETA1, "42", false); + assert_eq!(beta_stop["metadata"]["resourceVersion"], "42"); + assert_eq!(beta_stop["spec"]["operatingMode"], "Suspended"); + assert!(beta_stop["spec"].get("replicas").is_none()); + + let alpha_start = sandbox_operating_state_patch(SANDBOX_VERSION_V1ALPHA1, "43", true); + assert_eq!(alpha_start["metadata"]["resourceVersion"], "43"); + assert_eq!(alpha_start["spec"]["replicas"], 1); + assert!(alpha_start["spec"].get("operatingMode").is_none()); + } + + #[test] + fn stop_timeout_includes_pod_grace_period_and_reconcile_headroom() { + let resource = ApiResource::from_gvk(&GroupVersionKind::gvk( + SANDBOX_GROUP, + SANDBOX_VERSION_V1BETA1, + SANDBOX_KIND, + )); + let mut sandbox = DynamicObject::new("sandbox", &resource); + + assert_eq!( + kubernetes_sandbox_stop_timeout(&sandbox), + Duration::from_secs(60), + "an omitted grace period uses the Kubernetes 30-second default" + ); + + sandbox.data = serde_json::json!({ + "spec": { + "podTemplate": { + "spec": {"terminationGracePeriodSeconds": 45} + } + } + }); + assert_eq!( + kubernetes_sandbox_stop_timeout(&sandbox), + Duration::from_secs(75) + ); + } + + #[test] + fn stop_poll_interval_backs_off_to_cap() { + let mut interval = STOP_INITIAL_POLL_INTERVAL; + let expected = [ + Duration::from_millis(500), + Duration::from_secs(1), + Duration::from_secs(2), + Duration::from_secs(2), + ]; + + for expected_interval in expected { + interval = next_stop_poll_interval(interval); + assert_eq!(interval, expected_interval); + } + } + + #[test] + fn stopped_status_requires_published_condition() { + let resource = ApiResource::from_gvk(&GroupVersionKind::gvk( + SANDBOX_GROUP, + SANDBOX_VERSION_V1ALPHA1, + SANDBOX_KIND, + )); + let mut sandbox = DynamicObject::new("sandbox", &resource); + sandbox.data = serde_json::json!({"status": {"replicas": 0}}); + + assert!( + !kubernetes_sandbox_has_stopped_condition(&sandbox), + "v1alpha1 omits a zero status replica count on the wire; it is not a usable completion signal" + ); + + sandbox.data = serde_json::json!({ + "status": { + "conditions": [{"type": "Suspended", "status": "True"}] + } + }); + assert!(kubernetes_sandbox_has_stopped_condition(&sandbox)); + } + + #[test] + fn stop_failure_only_rejects_terminal_suspension_condition() { + let resource = ApiResource::from_gvk(&GroupVersionKind::gvk( + SANDBOX_GROUP, + SANDBOX_VERSION_V1BETA1, + SANDBOX_KIND, + )); + let mut sandbox = DynamicObject::new("sandbox", &resource); + sandbox.data = serde_json::json!({ + "status": { + "conditions": [{ + "type": "Suspended", + "status": "False", + "reason": "PodNotOwned", + "message": "Refused to delete pod because it is not owned by this sandbox" + }] + } + }); + + assert_eq!( + kubernetes_sandbox_stop_failure(&sandbox).as_deref(), + Some( + "Kubernetes sandbox stop rejected: Refused to delete pod because it is not owned by this sandbox" + ) + ); + + sandbox.data["status"]["conditions"][0]["status"] = serde_json::json!("Unknown"); + sandbox.data["status"]["conditions"][0]["reason"] = serde_json::json!("PodStateUnknown"); + assert!( + kubernetes_sandbox_stop_failure(&sandbox).is_none(), + "an unknown pod state can recover on a later controller reconciliation" + ); + } + #[test] fn sandbox_api_version_probe_keeps_non_404_errors() { let err = kube_api_error(403, "sandboxes.agents.x-k8s.io is forbidden"); diff --git a/crates/openshell-driver-kubernetes/src/grpc.rs b/crates/openshell-driver-kubernetes/src/grpc.rs index 6eeb51cd73..ef2e2686e4 100644 --- a/crates/openshell-driver-kubernetes/src/grpc.rs +++ b/crates/openshell-driver-kubernetes/src/grpc.rs @@ -8,9 +8,10 @@ use openshell_core::proto::compute::v1::{ CreateSandboxRequest, CreateSandboxResponse, DeleteSandboxRequest, DeleteSandboxResponse, GetCapabilitiesRequest, GetCapabilitiesResponse, GetGatewayListenerRequirementsRequest, GetGatewayListenerRequirementsResponse, GetSandboxRequest, GetSandboxResponse, - ListSandboxesRequest, ListSandboxesResponse, StopSandboxRequest, StopSandboxResponse, - ValidateSandboxCreateRequest, ValidateSandboxCreateResponse, WatchSandboxesEvent, - WatchSandboxesRequest, compute_driver_server::ComputeDriver, + ListSandboxesRequest, ListSandboxesResponse, StartSandboxRequest, StartSandboxResponse, + StopSandboxRequest, StopSandboxResponse, ValidateSandboxCreateRequest, + ValidateSandboxCreateResponse, WatchSandboxesEvent, WatchSandboxesRequest, + compute_driver_server::ComputeDriver, }; use std::pin::Pin; use tonic::{Request, Response, Status}; @@ -112,11 +113,32 @@ impl ComputeDriver for ComputeDriverService { async fn stop_sandbox( &self, - _request: Request, + request: Request, ) -> Result, Status> { - Err(Status::unimplemented( - "stop sandbox is not implemented by the kubernetes compute driver", - )) + let request = request.into_inner(); + if request.sandbox_id.is_empty() { + return Err(Status::invalid_argument("sandbox_id is required")); + } + self.driver + .stop_sandbox(&request.sandbox_id) + .await + .map_err(kubernetes_lifecycle_status)?; + Ok(Response::new(StopSandboxResponse {})) + } + + async fn start_sandbox( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + if request.sandbox_id.is_empty() { + return Err(Status::invalid_argument("sandbox_id is required")); + } + self.driver + .start_sandbox(&request.sandbox_id) + .await + .map_err(kubernetes_lifecycle_status)?; + Ok(Response::new(StartSandboxResponse {})) } async fn delete_sandbox( @@ -152,6 +174,14 @@ impl ComputeDriver for ComputeDriverService { } } +fn kubernetes_lifecycle_status(message: String) -> Status { + if message == "sandbox not found" { + Status::not_found(message) + } else { + Status::internal(message) + } +} + #[cfg(test)] mod tests { use crate::KubernetesDriverError; diff --git a/crates/openshell-driver-podman/README.md b/crates/openshell-driver-podman/README.md index 965a295d19..4cec6a4371 100644 --- a/crates/openshell-driver-podman/README.md +++ b/crates/openshell-driver-podman/README.md @@ -19,6 +19,15 @@ independently. For a rootless networking deep dive, see [NETWORKING.md](NETWORKING.md). +## Stop and Start + +Stop stops the managed container without deleting it. The per-sandbox named +workspace volume, token and proxy-auth secrets, labels, and container metadata +remain intact. Start starts the same container and reuses the same named +volume. Stopped managed containers remain visible through list and watch +reconciliation. Delete remains responsible for removing the container, +driver-owned secrets, and workspace volume. + ## Architecture The Podman driver communicates with the Podman daemon over a Unix socket and diff --git a/crates/openshell-driver-podman/src/driver.rs b/crates/openshell-driver-podman/src/driver.rs index 51c689fb29..8415a74ec4 100644 --- a/crates/openshell-driver-podman/src/driver.rs +++ b/crates/openshell-driver-podman/src/driver.rs @@ -3,11 +3,12 @@ //! Podman compute driver. -use crate::client::{PodmanApiError, PodmanClient, VolumeInspect}; +use crate::client::{ContainerListEntry, PodmanApiError, PodmanClient, VolumeInspect}; use crate::config::PodmanComputeConfig; use crate::container::{self, LABEL_MANAGED_FILTER, LABEL_SANDBOX_ID, PodmanSandboxDriverConfig}; use crate::watcher::{ - self, WatchStream, driver_sandbox_from_inspect, driver_sandbox_from_list_entry, + self, LifecycleEventFences, WatchStream, driver_sandbox_from_inspect, + driver_sandbox_from_list_entry, }; use openshell_core::ComputeDriverError; use openshell_core::config::CDI_GPU_DEVICE_ALL; @@ -36,7 +37,7 @@ impl From for ComputeDriverError { fn from(value: PodmanApiError) -> Self { match value { PodmanApiError::Conflict(_) => Self::AlreadyExists, - PodmanApiError::NotFound(msg) => Self::Message(format!("not found: {msg}")), + PodmanApiError::NotFound(_) => Self::NotFound, other => Self::Message(other.to_string()), } } @@ -56,6 +57,7 @@ pub struct PodmanComputeDriver { rootless_network_cmd: String, gpu_selector: Arc, gpu_inventory_refresh: Arc (CdiGpuInventory, bool) + Send + Sync>, + lifecycle_event_fences: LifecycleEventFences, } impl std::fmt::Debug for PodmanComputeDriver { @@ -396,6 +398,7 @@ impl PodmanComputeDriver { allow_all_default_gpu, )), gpu_inventory_refresh: Arc::new(local_podman_gpu_selector_state), + lifecycle_event_fences: LifecycleEventFences::default(), }) } @@ -815,20 +818,32 @@ impl PodmanComputeDriver { &self, sandbox_id: &str, ) -> Result, ComputeDriverError> { + Ok(self.find_container(sandbox_id).await?.map(|entry| entry.id)) + } + + async fn find_container( + &self, + sandbox_id: &str, + ) -> Result, ComputeDriverError> { let id_filter = format!("{LABEL_SANDBOX_ID}={sandbox_id}"); let entries = self .client .list_containers(&[LABEL_MANAGED_FILTER, &id_filter]) .await .map_err(ComputeDriverError::from)?; - Ok(entries.first().map(|e| e.id.clone())) + Ok(entries.into_iter().next()) } /// Stop a sandbox container without deleting it. pub async fn stop_sandbox(&self, sandbox_id: &str) -> Result<(), ComputeDriverError> { - let container_id = self.find_container_id(sandbox_id).await?.ok_or_else(|| { - ComputeDriverError::Precondition("sandbox container not found".into()) - })?; + let container = self + .find_container(sandbox_id) + .await? + .ok_or(ComputeDriverError::NotFound)?; + if container.state != "running" { + return Ok(()); + } + let container_id = container.id; info!(sandbox_id = %sandbox_id, container = %container_id, "Stopping sandbox container"); self.client @@ -837,6 +852,36 @@ impl PodmanComputeDriver { .map_err(ComputeDriverError::from) } + /// Start a previously stopped sandbox container. + pub async fn start_sandbox(&self, sandbox_id: &str) -> Result<(), ComputeDriverError> { + let container = self + .find_container(sandbox_id) + .await? + .ok_or(ComputeDriverError::NotFound)?; + if container.state == "running" { + return Ok(()); + } + let container_id = container.id; + info!(sandbox_id = %sandbox_id, container = %container_id, "Starting sandbox container"); + + // Fence delayed stop/die events from the previous container run before + // issuing the start. Podman's event stream can deliver those events + // after this API call has begun. Use the container's own transition + // timestamp so this remains correct for remote Podman services whose + // wall clock may differ from the gateway host. + let previous = self + .client + .inspect_container(&container_id) + .await + .map_err(ComputeDriverError::from)?; + self.lifecycle_event_fences + .record_previous_exit(sandbox_id, previous.state.finished_at.as_deref()); + self.client + .start_container(&container_id) + .await + .map_err(ComputeDriverError::from) + } + /// Delete a sandbox container and its workspace volume. pub async fn delete_sandbox(&self, sandbox_id: &str) -> Result { if sandbox_id.is_empty() { @@ -858,6 +903,7 @@ impl PodmanComputeDriver { &container::proxy_auth_secret_name(sandbox_id), ) .await; + self.lifecycle_event_fences.remove(sandbox_id); return Ok(false); }; info!(sandbox_id = %sandbox_id, container = %container_id, "Deleting sandbox container"); @@ -891,6 +937,7 @@ impl PodmanComputeDriver { &container::proxy_auth_secret_name(sandbox_id), ) .await; + self.lifecycle_event_fences.remove(sandbox_id); Ok(container_existed) } @@ -977,7 +1024,7 @@ impl PodmanComputeDriver { /// Start watching all managed sandbox containers. pub async fn watch_sandboxes(&self) -> Result { - watcher::start_watch(self.client.clone()) + watcher::start_watch(self.client.clone(), self.lifecycle_event_fences.clone()) .await .map_err(ComputeDriverError::from) } @@ -1016,6 +1063,7 @@ impl PodmanComputeDriver { gpu_inventory_refresh: Arc::new(move || { (refresh_inventory.clone(), allow_all_default_gpu) }), + lifecycle_event_fences: LifecycleEventFences::default(), } } } @@ -1172,7 +1220,64 @@ mod tests { #[test] fn podman_driver_error_from_not_found() { let err = ComputeDriverError::from(PodmanApiError::NotFound("gone".into())); - assert!(matches!(err, ComputeDriverError::Message(_))); + assert!(matches!(err, ComputeDriverError::NotFound)); + } + + #[tokio::test] + async fn stop_and_start_target_the_existing_container() { + let (stop_socket, stop_requests, stop_handle) = spawn_podman_stub( + "lifecycle-stop", + vec![ + StubResponse::new(StatusCode::OK, r#"[{"Id":"ctr-1","State":"running"}]"#), + StubResponse::new(StatusCode::NO_CONTENT, ""), + ], + ); + test_driver(stop_socket.clone()) + .stop_sandbox("sandbox-1") + .await + .expect("stop should succeed"); + stop_handle.await.expect("stop stub should finish"); + assert_eq!( + stop_requests + .lock() + .expect("request log lock should not be poisoned")[1], + format!( + "POST {}", + api_path("/libpod/containers/ctr-1/stop?timeout=10") + ) + ); + + let (start_socket, start_requests, start_handle) = spawn_podman_stub( + "lifecycle-start", + vec![ + StubResponse::new(StatusCode::OK, r#"[{"Id":"ctr-1","State":"stopped"}]"#), + StubResponse::new( + StatusCode::OK, + r#"{"Id":"ctr-1","Name":"sandbox","State":{"Status":"exited","Running":false,"FinishedAt":"2026-08-12T16:39:13Z"},"Config":{}}"#, + ), + StubResponse::new(StatusCode::NO_CONTENT, ""), + ], + ); + test_driver(start_socket.clone()) + .start_sandbox("sandbox-1") + .await + .expect("start should succeed"); + start_handle.await.expect("start stub should finish"); + assert_eq!( + start_requests + .lock() + .expect("request log lock should not be poisoned")[1], + format!("GET {}", api_path("/libpod/containers/ctr-1/json")) + ); + assert_eq!( + start_requests + .lock() + .expect("request log lock should not be poisoned")[2], + format!("POST {}", api_path("/libpod/containers/ctr-1/start")) + ); + + let _ = fs::remove_file(stop_socket); + let _ = fs::remove_file(start_socket); } #[test] diff --git a/crates/openshell-driver-podman/src/grpc.rs b/crates/openshell-driver-podman/src/grpc.rs index 2d0792d447..8d34660514 100644 --- a/crates/openshell-driver-podman/src/grpc.rs +++ b/crates/openshell-driver-podman/src/grpc.rs @@ -8,9 +8,10 @@ use openshell_core::proto::compute::v1::{ CreateSandboxRequest, CreateSandboxResponse, DeleteSandboxRequest, DeleteSandboxResponse, GetCapabilitiesRequest, GetCapabilitiesResponse, GetGatewayListenerRequirementsRequest, GetGatewayListenerRequirementsResponse, GetSandboxRequest, GetSandboxResponse, - ListSandboxesRequest, ListSandboxesResponse, StopSandboxRequest, StopSandboxResponse, - ValidateSandboxCreateRequest, ValidateSandboxCreateResponse, WatchSandboxesEvent, - WatchSandboxesRequest, compute_driver_server::ComputeDriver, + ListSandboxesRequest, ListSandboxesResponse, StartSandboxRequest, StartSandboxResponse, + StopSandboxRequest, StopSandboxResponse, ValidateSandboxCreateRequest, + ValidateSandboxCreateResponse, WatchSandboxesEvent, WatchSandboxesRequest, + compute_driver_server::ComputeDriver, }; use std::pin::Pin; use tonic::{Request, Response, Status}; @@ -127,6 +128,21 @@ impl ComputeDriver for ComputeDriverService { Ok(Response::new(StopSandboxResponse {})) } + async fn start_sandbox( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + if request.sandbox_id.is_empty() { + return Err(Status::invalid_argument("sandbox_id is required")); + } + self.driver + .start_sandbox(&request.sandbox_id) + .await + .map_err(Status::from)?; + Ok(Response::new(StartSandboxResponse {})) + } + async fn delete_sandbox( &self, request: Request, @@ -181,6 +197,12 @@ mod tests { assert_eq!(status.code(), tonic::Code::AlreadyExists); } + #[test] + fn not_found_driver_errors_map_to_not_found_status() { + let status: Status = ComputeDriverError::NotFound.into(); + assert_eq!(status.code(), tonic::Code::NotFound); + } + fn test_service(socket_path: PathBuf) -> ComputeDriverService { let config = PodmanComputeConfig { socket_path: Some(socket_path), diff --git a/crates/openshell-driver-podman/src/watcher.rs b/crates/openshell-driver-podman/src/watcher.rs index 4d397eb972..3e98d16271 100644 --- a/crates/openshell-driver-podman/src/watcher.rs +++ b/crates/openshell-driver-podman/src/watcher.rs @@ -16,7 +16,9 @@ use openshell_core::proto::compute::v1::{ DriverCondition, DriverSandbox, DriverSandboxStatus, WatchSandboxesDeletedEvent, WatchSandboxesEvent, WatchSandboxesSandboxEvent, watch_sandboxes_event, }; +use std::collections::HashMap; use std::pin::Pin; +use std::sync::{Arc, Mutex}; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; use tracing::{debug, info, warn}; @@ -30,6 +32,63 @@ const CONDITION_STOPPED: &str = "ContainerStopped"; pub type WatchStream = Pin> + Send>>; +/// Per-sandbox container exit timestamps that fence state changes from an earlier run. +/// +/// Podman can deliver a container's `die` or `stop` event after the stop API +/// has returned. If a restart is already in progress, inspecting the container +/// for that delayed event can report the previous exit and incorrectly regress +/// the sandbox from `Starting` to `Error`. +#[derive(Clone, Debug, Default)] +pub struct LifecycleEventFences { + previous_finished_at: Arc>>, +} + +impl LifecycleEventFences { + pub fn record_previous_exit(&self, sandbox_id: &str, finished_at: Option<&str>) { + let mut fences = self + .previous_finished_at + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + match finished_at.filter(|finished_at| !finished_at.is_empty()) { + Some(finished_at) => { + fences.insert(sandbox_id.to_string(), finished_at.to_string()); + } + None => { + fences.remove(sandbox_id); + } + } + } + + pub fn remove(&self, sandbox_id: &str) { + self.previous_finished_at + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .remove(sandbox_id); + } + + fn matches_previous_exit( + &self, + event: &PodmanEvent, + sandbox_id: &str, + state: &ContainerState, + ) -> bool { + if !matches!(event.action.as_str(), "die" | "stop") + || !matches!(state.status.as_str(), "exited" | "stopped") + { + return false; + } + + let Some(finished_at) = state.finished_at.as_deref() else { + return false; + }; + self.previous_finished_at + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(sandbox_id) + .is_some_and(|previous| previous == finished_at) + } +} + /// Build a `WatchSandboxesEvent` carrying a sandbox snapshot. fn sandbox_event(sandbox: DriverSandbox) -> WatchSandboxesEvent { WatchSandboxesEvent { @@ -71,7 +130,10 @@ fn deleted_event(sandbox_id: String) -> WatchSandboxesEvent { /// **Do not add reconnection logic inside this function.** A local reconnect /// would race with `watch_loop`'s retry and produce duplicate initial-sync /// events that corrupt the server's sandbox index. -pub async fn start_watch(client: PodmanClient) -> Result { +pub async fn start_watch( + client: PodmanClient, + lifecycle_event_fences: LifecycleEventFences, +) -> Result { let (tx, rx) = mpsc::channel::>(256); // 1. Subscribe to events first so we don't miss any during the list. @@ -120,7 +182,8 @@ pub async fn start_watch(client: PodmanClient) -> Result { - if let Some(we) = map_podman_event(&event, &client).await + if let Some(we) = + map_podman_event(&event, &client, &lifecycle_event_fences).await && tx.send(Ok(we)).await.is_err() { return; @@ -165,6 +228,7 @@ pub async fn start_watch(client: PodmanClient) -> Result Option { let container_id = &event.actor.id; let sandbox_id = event @@ -188,7 +252,24 @@ async fn map_podman_event( "create" | "start" | "stop" | "die" | "health_status" => { // Inspect the container to get current state. match client.inspect_container(container_id).await { - Ok(inspect) => driver_sandbox_from_inspect(&inspect).map(sandbox_event), + Ok(inspect) => { + if lifecycle_event_fences.matches_previous_exit( + event, + &sandbox_id, + &inspect.state, + ) { + debug!( + sandbox_id, + container_id = %container_id, + action = %event.action, + finished_at = inspect.state.finished_at.as_deref().unwrap_or_default(), + "Ignoring container stop event from before the latest sandbox start" + ); + None + } else { + driver_sandbox_from_inspect(&inspect).map(sandbox_event) + } + } Err(PodmanApiError::NotFound(_)) => { // The container is already gone by the time we inspected // it. This is a normal race between the `die`/`stop` event @@ -405,6 +486,66 @@ fn condition_from_state(state: &ContainerState) -> DriverCondition { mod tests { use super::*; + fn podman_event(action: &str, sandbox_id: &str, time_nano: i64) -> PodmanEvent { + PodmanEvent { + event_type: "container".to_string(), + action: action.to_string(), + actor: crate::client::EventActor { + id: "container-1".to_string(), + attributes: HashMap::from([(LABEL_SANDBOX_ID.to_string(), sandbox_id.to_string())]), + }, + time_nano, + } + } + + #[test] + fn lifecycle_fence_rejects_delayed_stop_events_from_before_restart() { + let fences = LifecycleEventFences::default(); + fences.record_previous_exit("sandbox-1", Some("2026-08-12T16:39:13Z")); + let previous_exit = ContainerState { + status: "exited".to_string(), + running: false, + exit_code: 137, + oom_killed: false, + health: None, + started_at: Some("2026-08-12T16:38:58Z".to_string()), + finished_at: Some("2026-08-12T16:39:13Z".to_string()), + }; + + assert!(fences.matches_previous_exit( + &podman_event("die", "sandbox-1", 199), + "sandbox-1", + &previous_exit, + )); + assert!(fences.matches_previous_exit( + &podman_event("stop", "sandbox-1", 200), + "sandbox-1", + &previous_exit, + )); + + let mut new_exit = previous_exit.clone(); + new_exit.finished_at = Some("2026-08-12T16:40:00Z".to_string()); + assert!(!fences.matches_previous_exit( + &podman_event("die", "sandbox-1", 201), + "sandbox-1", + &new_exit, + )); + + let mut running = previous_exit.clone(); + running.status = "running".to_string(); + running.finished_at = None; + assert!(!fences.matches_previous_exit( + &podman_event("die", "sandbox-1", 201), + "sandbox-1", + &running, + )); + assert!(!fences.matches_previous_exit( + &podman_event("start", "sandbox-1", 199), + "sandbox-1", + &previous_exit, + )); + } + #[test] fn condition_healthy_container() { let state = ContainerState { @@ -467,7 +608,7 @@ mod tests { #[test] fn sandbox_event_from_list_entry_running() { - let mut labels = std::collections::HashMap::new(); + let mut labels = HashMap::new(); labels.insert(LABEL_SANDBOX_ID.to_string(), "test-id".to_string()); labels.insert(LABEL_SANDBOX_NAME.to_string(), "test-name".to_string()); labels.insert(LABEL_SANDBOX_WORKSPACE.to_string(), "default".to_string()); diff --git a/crates/openshell-driver-vm/README.md b/crates/openshell-driver-vm/README.md index 23f75227a7..10ffac2ba1 100644 --- a/crates/openshell-driver-vm/README.md +++ b/crates/openshell-driver-vm/README.md @@ -197,7 +197,14 @@ The driver also writes the accepted `DriverSandbox` launch request to new VM driver process; that process scans the sandbox state directories, restarts each persisted VM launcher, and preserves any existing `overlay.ext4` instead of cloning a fresh overlay template. If a restart happened before the -overlay was created, the driver creates it during the resume attempt. +overlay was created, the driver creates it during the start attempt. + +Stop writes a marker in the sandbox state directory before terminating +the launcher and releasing host GPU and network allocations. It retains +`sandbox.pb`, `overlay.ext4`, and lifecycle-extension state. Startup registers +marked sandboxes without launching compute. Start removes the marker and uses +the normal persisted restore path with the existing overlay. Delete removes the +entire sandbox state directory, including a stop marker and overlay. ## Logs and debugging diff --git a/crates/openshell-driver-vm/src/driver.rs b/crates/openshell-driver-vm/src/driver.rs index 9b6c0dd6ce..ce4e1d2d9c 100644 --- a/crates/openshell-driver-vm/src/driver.rs +++ b/crates/openshell-driver-vm/src/driver.rs @@ -43,10 +43,10 @@ use openshell_core::proto::compute::v1::{ DriverSandboxTemplate as SandboxTemplate, GetCapabilitiesRequest, GetCapabilitiesResponse, GetGatewayListenerRequirementsRequest, GetGatewayListenerRequirementsResponse, GetSandboxRequest, GetSandboxResponse, ListSandboxesRequest, ListSandboxesResponse, - StopSandboxRequest, StopSandboxResponse, ValidateSandboxCreateRequest, - ValidateSandboxCreateResponse, WatchSandboxesDeletedEvent, WatchSandboxesEvent, - WatchSandboxesPlatformEvent, WatchSandboxesRequest, WatchSandboxesSandboxEvent, - compute_driver_server::ComputeDriver, watch_sandboxes_event, + StartSandboxRequest, StartSandboxResponse, StopSandboxRequest, StopSandboxResponse, + ValidateSandboxCreateRequest, ValidateSandboxCreateResponse, WatchSandboxesDeletedEvent, + WatchSandboxesEvent, WatchSandboxesPlatformEvent, WatchSandboxesRequest, + WatchSandboxesSandboxEvent, compute_driver_server::ComputeDriver, watch_sandboxes_event, }; use openshell_core::proto_struct::{ deserialize_optional_non_empty_string_list, struct_to_json_value, @@ -166,6 +166,7 @@ const OVERLAY_TEMPLATE_CACHE_DIR: &str = "overlay-templates"; const OVERLAY_TEMPLATE_CACHE_LAYOUT_VERSION: &str = "sandbox-overlay-ext4-v1"; const SANDBOX_OVERLAY_IMAGE: &str = "overlay.ext4"; const SANDBOX_REQUEST_FILE: &str = "sandbox.pb"; +const SANDBOX_STOPPED_FILE: &str = "stopped"; const GUEST_IMAGE_CONFIG_DIR: &str = "openshell-image"; const GUEST_IMAGE_OCI_LAYOUT_DIR: &str = "oci"; const GUEST_IMAGE_OCI_REF: &str = "openshell"; @@ -604,7 +605,7 @@ impl VmDriver { registry.remove(&sandbox.id); let _ = tokio::fs::remove_dir_all(&state_dir).await; return Err(Status::internal(format!( - "write sandbox resume metadata failed: {err}" + "write sandbox start metadata failed: {err}" ))); } @@ -1058,6 +1059,129 @@ impl VmDriver { Ok(()) } + pub async fn stop_sandbox(&self, sandbox_id: &str, sandbox_name: &str) -> Result<(), Status> { + if !sandbox_id.is_empty() { + validate_sandbox_id(sandbox_id)?; + } + let record_id = { + let registry = self.registry.lock().await; + if registry.contains_key(sandbox_id) { + Some(sandbox_id.to_string()) + } else { + registry + .iter() + .find(|(_, record)| record.snapshot.name == sandbox_name) + .map(|(id, _)| id.clone()) + } + } + .ok_or_else(|| Status::not_found("sandbox not found"))?; + + let state_dir = { + let registry = self.registry.lock().await; + registry + .get(&record_id) + .ok_or_else(|| Status::not_found("sandbox not found"))? + .state_dir + .clone() + }; + + // Persist intent before detaching process handles or releasing host + // allocations. If this write fails, the live record remains intact. + tokio::fs::write(state_dir.join(SANDBOX_STOPPED_FILE), b"stopped\n") + .await + .map_err(|err| Status::internal(format!("persist stop marker failed: {err}")))?; + + let (process, provisioning_task, has_gpu, has_qemu_network, snapshot) = { + let mut registry = self.registry.lock().await; + let record = registry + .get_mut(&record_id) + .ok_or_else(|| Status::not_found("sandbox not found"))?; + ( + record.process.take(), + record.provisioning_task.take(), + record.gpu_bdf.take().is_some(), + std::mem::take(&mut record.qemu_network_allocated), + record.snapshot.clone(), + ) + }; + + if let Some(task) = provisioning_task { + task.abort(); + } + if let Some(process) = process { + let mut process = process.lock().await; + process.deleting = true; + terminate_vm_process(&mut process.child) + .await + .map_err(|err| Status::internal(format!("failed to stop vm: {err}")))?; + } + self.lifecycle_extensions + .after_launch_failed(&snapshot, &state_dir, LaunchAbortReason::Stopped) + .await; + self.release_allocations(&record_id, has_gpu, has_qemu_network); + + if let Some(snapshot) = self + .set_snapshot_condition(&record_id, stopped_condition(), false) + .await + { + self.publish_snapshot(snapshot); + } + self.publish_platform_event( + record_id, + platform_event("vm", "Normal", "Stopped", "VM sandbox stopped".to_string()), + ); + Ok(()) + } + + pub async fn start_sandbox(&self, sandbox_id: &str, sandbox_name: &str) -> Result<(), Status> { + if !sandbox_id.is_empty() { + validate_sandbox_id(sandbox_id)?; + } + let (record_id, state_dir, already_running) = { + let registry = self.registry.lock().await; + let (id, record) = if let Some(entry) = registry.get_key_value(sandbox_id) { + entry + } else { + registry + .iter() + .find(|(_, record)| record.snapshot.name == sandbox_name) + .ok_or_else(|| Status::not_found("sandbox not found"))? + }; + ( + id.clone(), + record.state_dir.clone(), + record.process.is_some() || record.provisioning_task.is_some(), + ) + }; + if already_running { + return Ok(()); + } + + let sandbox = read_sandbox_request(&state_dir.join(SANDBOX_REQUEST_FILE)) + .await + .map_err(|err| { + Status::internal(format!("read sandbox start metadata failed: {err}")) + })?; + let stopped_record = self + .registry + .lock() + .await + .remove(&record_id) + .ok_or_else(|| Status::not_found("sandbox not found"))?; + let restored = self + .restore_persisted_sandbox(sandbox, state_dir, true, &tracing::Span::current()) + .await; + if !restored { + self.registry + .lock() + .await + .entry(record_id) + .or_insert(stopped_record); + return Err(Status::internal("failed to start persisted VM sandbox")); + } + Ok(()) + } + #[tracing::instrument( name = "vm.delete", skip(self), @@ -1264,24 +1388,50 @@ impl VmDriver { continue; } - self.restore_persisted_sandbox(sandbox, state_dir, &tracing::Span::current()) + if tokio::fs::metadata(state_dir.join(SANDBOX_STOPPED_FILE)) + .await + .is_ok() + { + let snapshot = sandbox_snapshot(&sandbox, stopped_condition(), false); + let mut registry = self.registry.lock().await; + registry.entry(sandbox.id.clone()).or_insert(SandboxRecord { + snapshot: snapshot.clone(), + state_dir: state_dir.clone(), + process: None, + provisioning_task: None, + gpu_bdf: None, + qemu_network_allocated: false, + deleting: false, + }); + drop(registry); + self.publish_snapshot(snapshot); + info!(sandbox_id = %sandbox.id, "vm driver: restored stopped sandbox without launching compute"); + continue; + } + + self.restore_persisted_sandbox(sandbox, state_dir, false, &tracing::Span::current()) .await; } } + /// Restore a persisted sandbox and report whether the driver accepted it. + /// For explicit start, the stop marker is cleared only after all + /// restore preflight checks pass and the replacement registry record is + /// installed. A failed restore therefore remains durably stopped. async fn restore_persisted_sandbox( &self, sandbox: Sandbox, state_dir: PathBuf, + clear_stop_marker: bool, reconciliation_span: &tracing::Span, - ) { + ) -> bool { let Some(image_ref) = self.resolved_sandbox_image(&sandbox) else { warn!( sandbox_id = %sandbox.id, sandbox_name = %sandbox.name, "vm driver: cannot restore persisted sandbox without image" ); - return; + return false; }; let tls_paths = match self.config.tls_paths() { Ok(paths) => paths, @@ -1292,7 +1442,7 @@ impl VmDriver { error = %err, "vm driver: cannot restore persisted sandbox TLS configuration" ); - return; + return false; } }; @@ -1304,7 +1454,7 @@ impl VmDriver { error = %err.message(), "vm driver: cannot restore persisted sandbox extension state" ); - return; + return false; } let persisted = RestoreContext { @@ -1319,14 +1469,14 @@ impl VmDriver { error = %err, "vm driver: lifecycle extension rejected persisted sandbox restore" ); - return; + return false; } let snapshot = sandbox_snapshot(&sandbox, provisioning_condition(), false); { let mut registry = self.registry.lock().await; if registry.contains_key(&sandbox.id) { - return; + return false; } registry.insert( sandbox.id.clone(), @@ -1342,6 +1492,23 @@ impl VmDriver { ); } + if clear_stop_marker { + match tokio::fs::remove_file(state_dir.join(SANDBOX_STOPPED_FILE)).await { + Ok(()) => {} + Err(err) if err.kind() == std::io::ErrorKind::NotFound => {} + Err(err) => { + self.registry.lock().await.remove(&sandbox.id); + warn!( + sandbox_id = %sandbox.id, + state_dir = %state_dir.display(), + error = %err, + "vm driver: cannot clear stop marker for persisted sandbox restore" + ); + return false; + } + } + } + self.publish_platform_event( sandbox.id.clone(), platform_event( @@ -1392,6 +1559,7 @@ impl VmDriver { } else { task.abort(); } + true } fn release_gpu(&self, sandbox_id: &str) { @@ -3179,11 +3347,22 @@ impl ComputeDriver for VmDriver { async fn stop_sandbox( &self, - _request: Request, + request: Request, ) -> Result, Status> { - Err(Status::unimplemented( - "stop sandbox is not implemented by the vm compute driver", - )) + let request = request.into_inner(); + self.stop_sandbox(&request.sandbox_id, &request.sandbox_name) + .await?; + Ok(Response::new(StopSandboxResponse {})) + } + + async fn start_sandbox( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + self.start_sandbox(&request.sandbox_id, &request.sandbox_name) + .await?; + Ok(Response::new(StartSandboxResponse {})) } async fn delete_sandbox( @@ -5184,6 +5363,16 @@ fn deleting_condition() -> SandboxCondition { } } +fn stopped_condition() -> SandboxCondition { + SandboxCondition { + r#type: "Stopped".to_string(), + status: "True".to_string(), + reason: "ComputeStopped".to_string(), + message: "VM compute is stopped and persistent state is retained".to_string(), + last_transition_time: String::new(), + } +} + fn error_condition(reason: &str, message: &str) -> SandboxCondition { SandboxCondition { r#type: "Ready".to_string(), @@ -6349,13 +6538,13 @@ mod tests { } #[tokio::test] - async fn sandbox_request_metadata_round_trips_for_resume() { + async fn sandbox_request_metadata_round_trips_for_start() { let base = unique_temp_dir(); let state_dir = base.join("sandboxes").join("sandbox-123"); std::fs::create_dir_all(&state_dir).unwrap(); let sandbox = Sandbox { id: "sandbox-123".to_string(), - name: "resume-sandbox".to_string(), + name: "start-sandbox".to_string(), namespace: "vm-dev".to_string(), spec: Some(SandboxSpec { environment: HashMap::from([("KEY".to_string(), "value".to_string())]), @@ -6395,8 +6584,64 @@ mod tests { let _ = std::fs::remove_dir_all(base); } + #[tokio::test] + async fn failed_start_preserves_stopped_state() { + let temp = tempfile::tempdir().unwrap(); + let mut driver = test_driver_with_extensions(LifecycleExtensionRegistry::new()); + driver.config.state_dir = temp.path().to_path_buf(); + let sandbox = Sandbox { + id: "sandbox-stopped".to_string(), + name: "stopped".to_string(), + ..Default::default() + }; + let state_dir = temp.path().join("sandboxes").join(&sandbox.id); + create_private_dir_all(&state_dir).await.unwrap(); + write_sandbox_request(&state_dir, &sandbox).await.unwrap(); + tokio::fs::write(state_dir.join(SANDBOX_STOPPED_FILE), b"stopped\n") + .await + .unwrap(); + let snapshot = sandbox_snapshot(&sandbox, stopped_condition(), false); + driver.registry.lock().await.insert( + sandbox.id.clone(), + SandboxRecord { + snapshot, + state_dir: state_dir.clone(), + process: None, + provisioning_task: None, + gpu_bdf: None, + qemu_network_allocated: false, + deleting: false, + }, + ); + + let err = driver + .start_sandbox(&sandbox.id, &sandbox.name) + .await + .expect_err("start without an image should fail"); + + assert_eq!(err.code(), Code::Internal); + assert!( + tokio::fs::metadata(state_dir.join(SANDBOX_STOPPED_FILE)) + .await + .is_ok(), + "failed start must retain its durable stop marker" + ); + let restored = driver + .get_sandbox(&sandbox.id, &sandbox.name) + .await + .unwrap() + .expect("failed start must retain its stopped registry record"); + let condition = restored + .status + .as_ref() + .and_then(|status| status.conditions.first()) + .expect("stopped condition"); + assert_eq!(condition.r#type, "Stopped"); + assert_eq!(condition.status, "True"); + } + #[test] - fn prepare_sandbox_overlay_preserves_existing_overlay_on_resume() { + fn prepare_sandbox_overlay_preserves_existing_overlay_on_start() { let base = unique_temp_dir(); std::fs::create_dir_all(&base).unwrap(); let template = base.join("template.ext4"); @@ -6420,7 +6665,7 @@ mod tests { } #[test] - fn prepare_sandbox_overlay_creates_missing_overlay_on_resume() { + fn prepare_sandbox_overlay_creates_missing_overlay_on_start() { let base = unique_temp_dir(); std::fs::create_dir_all(&base).unwrap(); let template = base.join("template.ext4"); diff --git a/crates/openshell-driver-vm/src/lifecycle.rs b/crates/openshell-driver-vm/src/lifecycle.rs index 646070c3a1..25ec91db67 100644 --- a/crates/openshell-driver-vm/src/lifecycle.rs +++ b/crates/openshell-driver-vm/src/lifecycle.rs @@ -32,6 +32,8 @@ pub enum LaunchAbortReason { /// opportunity to release host resources they allocated in /// [`LifecycleExtension::before_launch`]. ProcessExited, + /// The gateway intentionally stopped the sandbox while retaining disk state. + Stopped, } #[derive(Debug, Clone)] diff --git a/crates/openshell-driver-vm/src/otel_tracing.rs b/crates/openshell-driver-vm/src/otel_tracing.rs index fac2720cd7..adfeb896c6 100644 --- a/crates/openshell-driver-vm/src/otel_tracing.rs +++ b/crates/openshell-driver-vm/src/otel_tracing.rs @@ -74,6 +74,7 @@ fn compute_driver_rpc_operation(path: &str) -> (&'static str, &'static str) { Some("GetSandbox") => ("driver.get_sandbox", "get_sandbox"), Some("ListSandboxes") => ("driver.list_sandboxes", "list_sandboxes"), Some("StopSandbox") => ("driver.stop_sandbox", "stop_sandbox"), + Some("StartSandbox") => ("driver.start_sandbox", "start_sandbox"), Some("DeleteSandbox") => ("driver.delete_sandbox", "delete_sandbox"), Some("WatchSandboxes") => ("driver.watch_sandboxes", "watch_sandboxes"), _ => ("driver.unknown", "unknown"), @@ -177,6 +178,7 @@ mod tests { ("GetSandbox", "driver.get_sandbox", "get_sandbox"), ("ListSandboxes", "driver.list_sandboxes", "list_sandboxes"), ("StopSandbox", "driver.stop_sandbox", "stop_sandbox"), + ("StartSandbox", "driver.start_sandbox", "start_sandbox"), ("DeleteSandbox", "driver.delete_sandbox", "delete_sandbox"), ( "WatchSandboxes", diff --git a/crates/openshell-sdk/src/client.rs b/crates/openshell-sdk/src/client.rs index 0924ae4d62..c67e91e219 100644 --- a/crates/openshell-sdk/src/client.rs +++ b/crates/openshell-sdk/src/client.rs @@ -217,6 +217,34 @@ impl OpenShellClient { Ok(response.deleted) } + /// Stop a sandbox by name. + pub async fn stop_sandbox(&self, name: &str) -> Result { + let response = self + .unary(|mut grpc| { + let request = proto::StopSandboxRequest { + name: name.to_string(), + workspace: String::new(), + }; + async move { grpc.stop_sandbox(request).await } + }) + .await?; + sandbox_from_response(response.sandbox) + } + + /// Start a stopped sandbox by name. + pub async fn start_sandbox(&self, name: &str) -> Result { + let response = self + .unary(|mut grpc| { + let request = proto::StartSandboxRequest { + name: name.to_string(), + workspace: String::new(), + }; + async move { grpc.start_sandbox(request).await } + }) + .await?; + sandbox_from_response(response.sandbox) + } + /// Poll [`OpenShellClient::get_sandbox`] until the sandbox reaches /// [`SandboxPhase::Ready`] or the `timeout` elapses. /// @@ -586,6 +614,36 @@ impl WorkspaceScopedClient { Ok(response.deleted) } + /// Stop a sandbox by name in this workspace. + pub async fn stop_sandbox(&self, name: &str) -> Result { + let response = self + .client + .unary(|mut grpc| { + let request = proto::StopSandboxRequest { + name: name.to_string(), + workspace: self.workspace.clone(), + }; + async move { grpc.stop_sandbox(request).await } + }) + .await?; + sandbox_from_response(response.sandbox) + } + + /// Start a stopped sandbox by name in this workspace. + pub async fn start_sandbox(&self, name: &str) -> Result { + let response = self + .client + .unary(|mut grpc| { + let request = proto::StartSandboxRequest { + name: name.to_string(), + workspace: self.workspace.clone(), + }; + async move { grpc.start_sandbox(request).await } + }) + .await?; + sandbox_from_response(response.sandbox) + } + /// Poll until the sandbox reaches [`SandboxPhase::Ready`] or the timeout /// elapses. pub async fn wait_ready(&self, name: &str, timeout: Duration) -> Result { diff --git a/crates/openshell-sdk/src/raw.rs b/crates/openshell-sdk/src/raw.rs index 80d7453602..e974b19259 100644 --- a/crates/openshell-sdk/src/raw.rs +++ b/crates/openshell-sdk/src/raw.rs @@ -26,7 +26,7 @@ pub use openshell_core::proto::{ ExecSandboxRequest, GetSandboxRequest, GetWorkspaceRequest, HealthRequest, ListProvidersRequest, ListSandboxesRequest, ListWorkspacesRequest, Sandbox, SandboxPhase as ProtoSandboxPhase, SandboxSpec as ProtoSandboxSpec, SandboxTemplate, - ServiceStatus as ProtoServiceStatus, Workspace, + ServiceStatus as ProtoServiceStatus, StartSandboxRequest, StopSandboxRequest, Workspace, }; /// Type alias for the gRPC client wrapped in the SDK's auth interceptor. diff --git a/crates/openshell-sdk/src/types.rs b/crates/openshell-sdk/src/types.rs index 2eb4ef0a92..6f179499c9 100644 --- a/crates/openshell-sdk/src/types.rs +++ b/crates/openshell-sdk/src/types.rs @@ -62,6 +62,9 @@ pub enum SandboxPhase { Error, Deleting, Unknown, + Stopping, + Stopped, + Starting, } impl From for SandboxPhase { @@ -73,6 +76,9 @@ impl From for SandboxPhase { proto::SandboxPhase::Error => Self::Error, proto::SandboxPhase::Deleting => Self::Deleting, proto::SandboxPhase::Unknown => Self::Unknown, + proto::SandboxPhase::Stopping => Self::Stopping, + proto::SandboxPhase::Stopped => Self::Stopped, + proto::SandboxPhase::Starting => Self::Starting, } } } diff --git a/crates/openshell-sdk/tests/client_mock.rs b/crates/openshell-sdk/tests/client_mock.rs index 4ff23d74d1..09e91330ce 100644 --- a/crates/openshell-sdk/tests/client_mock.rs +++ b/crates/openshell-sdk/tests/client_mock.rs @@ -31,6 +31,8 @@ struct MockState { last_create: Mutex>, last_delete_name: Mutex>, last_delete_workspace: Mutex>, + last_stop: Mutex>, + last_start: Mutex>, last_list_request: Mutex>, last_exec_request: Mutex>, last_workspace_request: Mutex>, @@ -161,6 +163,38 @@ impl OpenShell for TestOpenShell { })) } + async fn stop_sandbox( + &self, + request: tonic::Request, + ) -> Result, Status> { + let request = request.into_inner(); + let sandbox = sandbox_with_phase_ws( + &request.name, + proto::SandboxPhase::Stopped, + &request.workspace, + ); + *self.state.last_stop.lock().await = Some(request); + Ok(Response::new(proto::SandboxResponse { + sandbox: Some(sandbox), + })) + } + + async fn start_sandbox( + &self, + request: tonic::Request, + ) -> Result, Status> { + let request = request.into_inner(); + let sandbox = sandbox_with_phase_ws( + &request.name, + proto::SandboxPhase::Starting, + &request.workspace, + ); + *self.state.last_start.lock().await = Some(request); + Ok(Response::new(proto::SandboxResponse { + sandbox: Some(sandbox), + })) + } + async fn get_sandbox( &self, request: tonic::Request, @@ -816,6 +850,29 @@ async fn delete_sandbox_returns_server_ack() { assert_eq!(observed.as_deref(), Some("doomed")); } +#[tokio::test] +async fn stop_and_start_map_requests_and_phases() { + let state = Arc::new(MockState::default()); + let endpoint = start_mock(state.clone()).await; + let client = connect(&endpoint).await; + + let stopped = client.stop_sandbox("sleepy").await.unwrap(); + assert_eq!(stopped.phase, SandboxPhase::Stopped); + let stop = state.last_stop.lock().await.clone().unwrap(); + assert_eq!(stop.name, "sleepy"); + assert!(stop.workspace.is_empty()); + + let started = client + .workspace("team-a") + .start_sandbox("sleepy") + .await + .unwrap(); + assert_eq!(started.phase, SandboxPhase::Starting); + let start = state.last_start.lock().await.clone().unwrap(); + assert_eq!(start.name, "sleepy"); + assert_eq!(start.workspace, "team-a"); +} + #[tokio::test] async fn wait_ready_transitions_through_phases() { let state = Arc::new(MockState { diff --git a/crates/openshell-server/src/auth/method_authz.rs b/crates/openshell-server/src/auth/method_authz.rs index c06ca09f69..71eb7acac6 100644 --- a/crates/openshell-server/src/auth/method_authz.rs +++ b/crates/openshell-server/src/auth/method_authz.rs @@ -148,4 +148,18 @@ mod tests { // Unknown method falls through to AuthzPolicy::check. assert!(is_user_callable("/openshell.v1.OpenShell/FutureMethod")); } + + #[test] + fn sandbox_lifecycle_mutations_require_user_write_authority() { + for path in [ + "/openshell.v1.OpenShell/StopSandbox", + "/openshell.v1.OpenShell/StartSandbox", + ] { + let entry = lookup(path).expect("lifecycle RPC must have auth metadata"); + assert_eq!(entry.auth_mode, AuthMode::Bearer); + assert_eq!(entry.scope.as_deref(), Some("sandbox:write")); + assert_eq!(entry.workspace_role.as_deref(), Some("user")); + assert!(!is_sandbox_callable(path)); + } + } } diff --git a/crates/openshell-server/src/auth/sandbox_methods.rs b/crates/openshell-server/src/auth/sandbox_methods.rs index a74b1280ce..5cc9e3693a 100644 --- a/crates/openshell-server/src/auth/sandbox_methods.rs +++ b/crates/openshell-server/src/auth/sandbox_methods.rs @@ -42,6 +42,8 @@ mod tests { assert!(!is_sandbox_callable( "/openshell.v1.OpenShell/DeleteSandbox" )); + assert!(!is_sandbox_callable("/openshell.v1.OpenShell/StopSandbox")); + assert!(!is_sandbox_callable("/openshell.v1.OpenShell/StartSandbox")); assert!(!is_sandbox_callable( "/openshell.v1.OpenShell/CreateProvider" )); diff --git a/crates/openshell-server/src/compute/mod.rs b/crates/openshell-server/src/compute/mod.rs index 3aaa0ddf8c..bc98670b89 100644 --- a/crates/openshell-server/src/compute/mod.rs +++ b/crates/openshell-server/src/compute/mod.rs @@ -38,10 +38,10 @@ use openshell_core::proto::compute::v1::{ GetCapabilitiesRequest, GetGatewayListenerRequirementsRequest, GetGatewayListenerRequirementsResponse, GetSandboxRequest, GpuResourceRequirements as DriverGpuResourceRequirements, ListSandboxesRequest, - ResourceRequirements as DriverSandboxResourceRequirements, ValidateSandboxCreateRequest, - WatchSandboxesEvent, WatchSandboxesRequest, compute_driver_client::ComputeDriverClient, - compute_driver_server::ComputeDriver, gateway_listener_requirement::Selector, - watch_sandboxes_event, + ResourceRequirements as DriverSandboxResourceRequirements, StartSandboxRequest, + StopSandboxRequest, ValidateSandboxCreateRequest, WatchSandboxesEvent, WatchSandboxesRequest, + compute_driver_client::ComputeDriverClient, compute_driver_server::ComputeDriver, + gateway_listener_requirement::Selector, watch_sandboxes_event, }; use openshell_core::proto::{ PlatformEvent, Sandbox, SandboxCondition, SandboxPhase, SandboxSpec, SandboxStatus, @@ -180,20 +180,20 @@ impl GatewayListenerRequirement { } } -/// Serializes request-side deletes for the same stable sandbox ID. +/// Serializes request-side lifecycle mutations for the same stable sandbox ID. /// /// Watch events deliberately do not use these gates, so a slow driver delete /// cannot block the sequential watch loop. Weak values let entries disappear /// after the last request using a sandbox's gate completes. #[derive(Debug, Default)] -struct DeleteGateRegistry { +struct LifecycleGateRegistry { gates: StdMutex>>>, } -impl DeleteGateRegistry { - async fn lock_for(&self, sandbox_id: &str) -> SandboxDeleteGuard { +impl LifecycleGateRegistry { + async fn lock_for(&self, sandbox_id: &str) -> SandboxLifecycleGuard { let gate = self.gate_for(sandbox_id); - SandboxDeleteGuard { + SandboxLifecycleGuard { _guard: gate.lock_owned().await, } } @@ -202,7 +202,7 @@ impl DeleteGateRegistry { let mut gates = self .gates .lock() - .expect("sandbox delete gate registry lock poisoned"); + .expect("sandbox lifecycle gate registry lock poisoned"); gates.retain(|_, gate| gate.strong_count() > 0); if let Some(gate) = gates.get(sandbox_id).and_then(Weak::upgrade) { @@ -218,18 +218,18 @@ impl DeleteGateRegistry { fn entry_count(&self) -> usize { self.gates .lock() - .expect("sandbox delete gate registry lock poisoned") + .expect("sandbox lifecycle gate registry lock poisoned") .len() } } -/// Proof that the current delete operation holds its sandbox-ID gate. +/// Proof that the current operation holds its sandbox-ID lifecycle gate. /// -/// Delete code must acquire this guard before taking `ComputeRuntime::sync_lock`. -/// Passing it to `lock_global_for_delete` makes that ordering visible at every -/// global-lock acquisition in the delete path. +/// Lifecycle code must acquire this guard before taking `ComputeRuntime::sync_lock`. +/// Passing it to `lock_global_for_lifecycle` makes that ordering visible at +/// every global-lock acquisition in a lifecycle path. #[derive(Debug)] -struct SandboxDeleteGuard { +struct SandboxLifecycleGuard { _guard: tokio::sync::OwnedMutexGuard<()>, } @@ -295,21 +295,21 @@ impl ShutdownCleanup for DockerComputeDriver { } } -/// Resume a single sandbox whose store record indicates it should be +/// Start a single sandbox whose store record indicates it should be /// running. Implemented by drivers (currently only Docker) where compute /// resources do not auto-restart with the gateway. Returns `Ok(true)` if -/// the backend resource was found and resumed (or was already running), +/// the backend resource was found and started (or was already running), /// `Ok(false)` if no backend resource exists. #[tonic::async_trait] -trait StartupResume: Send + Sync { - async fn resume_sandbox(&self, sandbox_id: &str, sandbox_name: &str) -> Result; +trait StartupSandboxStarter: Send + Sync { + async fn start_sandbox(&self, sandbox_id: &str, sandbox_name: &str) -> Result; } #[tonic::async_trait] #[cfg(not(target_os = "windows"))] -impl StartupResume for DockerComputeDriver { - async fn resume_sandbox(&self, sandbox_id: &str, sandbox_name: &str) -> Result { - Self::resume_sandbox(self, sandbox_id, sandbox_name) +impl StartupSandboxStarter for DockerComputeDriver { + async fn start_sandbox(&self, sandbox_id: &str, sandbox_name: &str) -> Result { + Self::start_sandbox(self, sandbox_id, sandbox_name) .await .map_err(|err| err.to_string()) } @@ -532,13 +532,22 @@ impl ComputeDriver for RemoteComputeDriver { async fn stop_sandbox( &self, - request: Request, + request: Request, ) -> Result, Status> { let mut client = self.client(); client.stop_sandbox(request).await } + async fn start_sandbox( + &self, + request: Request, + ) -> Result, Status> + { + let mut client = self.client(); + client.start_sandbox(request).await + } + async fn delete_sandbox( &self, request: Request, @@ -564,7 +573,7 @@ pub struct ComputeRuntime { driver: TracedDriver, driver_info: ComputeDriverInfoSnapshot, shutdown_cleanup: Option>, - startup_resume: Option>, + startup_starter: Option>, driver_process: Option>, default_image: String, store: Arc, @@ -573,7 +582,7 @@ pub struct ComputeRuntime { tracing_log_bus: TracingLogBus, supervisor_sessions: Arc, sync_lock: Arc>, - delete_gates: Arc, + lifecycle_gates: Arc, gateway_listener_requirements: Vec, replica_id: String, } @@ -599,7 +608,7 @@ impl ComputeRuntime { driver_name: String, driver: SharedComputeDriver, shutdown_cleanup: Option>, - startup_resume: Option>, + startup_starter: Option>, driver_process: Option>, store: Arc, sandbox_index: SandboxIndex, @@ -685,7 +694,7 @@ impl ComputeRuntime { driver: TracedDriver::new(driver, driver_name), driver_info, shutdown_cleanup, - startup_resume, + startup_starter, driver_process, default_image, store, @@ -694,7 +703,7 @@ impl ComputeRuntime { tracing_log_bus, supervisor_sessions, sync_lock: Arc::new(Mutex::new(())), - delete_gates: Arc::new(DeleteGateRegistry::default()), + lifecycle_gates: Arc::new(LifecycleGateRegistry::default()), gateway_listener_requirements, replica_id: lease::replica_id(), }) @@ -711,18 +720,18 @@ impl ComputeRuntime { } /// Acquires the process-wide lock for code that already holds the - /// sandbox-ID delete gate. The guard parameter documents and enforces that - /// delete-path callers acquire locks in delete-gate -> global-lock order. - async fn lock_global_for_delete( + /// sandbox-ID lifecycle gate. The guard parameter documents and enforces + /// that callers acquire locks in lifecycle-gate -> global-lock order. + async fn lock_global_for_lifecycle( &self, - _delete_guard: &SandboxDeleteGuard, + _lifecycle_guard: &SandboxLifecycleGuard, ) -> tokio::sync::OwnedMutexGuard<()> { self.sync_lock.clone().lock_owned().await } #[cfg(test)] - pub(crate) fn delete_gate_entry_count(&self) -> usize { - self.delete_gates.entry_count() + pub(crate) fn lifecycle_gate_entry_count(&self) -> usize { + self.lifecycle_gates.entry_count() } #[cfg(not(target_os = "windows"))] @@ -741,13 +750,13 @@ impl ComputeRuntime { .map_err(|err| ComputeError::Message(err.to_string()))?, ); let shutdown_cleanup: Arc = driver.clone(); - let startup_resume: Arc = driver.clone(); + let startup_starter: Arc = driver.clone(); let driver: SharedComputeDriver = driver; Self::from_driver( ComputeDriverKind::Docker.as_str().to_string(), driver, Some(shutdown_cleanup), - Some(startup_resume), + Some(startup_starter), None, store, sandbox_index, @@ -981,6 +990,441 @@ impl ComputeRuntime { } } + pub(crate) async fn stop_sandbox( + &self, + workspace: &str, + name: &str, + ) -> Result { + let candidate = self + .store + .get_message_by_name::(workspace, name) + .await + .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? + .ok_or_else(|| Status::not_found("sandbox not found"))?; + let sandbox_id = candidate.object_id().to_string(); + let sandbox_name = candidate.object_name().to_string(); + let lifecycle_guard = self.lifecycle_gates.lock_for(&sandbox_id).await; + let global_guard = self.lock_global_for_lifecycle(&lifecycle_guard).await; + let current = self + .store + .get_message::(&sandbox_id) + .await + .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? + .ok_or_else(|| Status::not_found("sandbox not found"))?; + if current.object_name() != sandbox_name { + return Err(Status::aborted( + "sandbox name changed while the stop request was waiting; retry explicitly", + )); + } + + let phase = SandboxPhase::try_from(current.phase()).unwrap_or(SandboxPhase::Unknown); + if phase == SandboxPhase::Stopped { + self.cleanup_stopped_sandbox_sessions(¤t) + .await + .map_err(Status::internal)?; + return Ok(current); + } + if !matches!(phase, SandboxPhase::Ready | SandboxPhase::Stopping) { + return Err(Status::failed_precondition(format!( + "sandbox must be Ready to stop (current phase: {phase:?})" + ))); + } + + let (previous, stopping) = if phase == SandboxPhase::Stopping { + // Acquiring the lifecycle gate proves that no local worker still + // owns this transition. Retry the idempotent driver operation. + (current.clone(), current) + } else { + let previous = current.clone(); + let stopping = self + .write_lifecycle_phase( + ¤t, + SandboxPhase::Stopping, + "Stopping", + "Sandbox stop requested", + ) + .await?; + self.sandbox_index.update_from_sandbox(&stopping); + self.sandbox_watch_bus.notify(&sandbox_id); + (previous, stopping) + }; + drop(global_guard); + + // Once the durable transition is committed, request cancellation must + // not cancel the driver operation and strand the sandbox in + // `Stopping`. Keep the lifecycle gate in an owned worker, matching + // the delete path's cancellation semantics. + let runtime = self.clone(); + let request_span = tracing::Span::current(); + tokio::spawn( + async move { + runtime + .complete_sandbox_stop( + sandbox_id, + sandbox_name, + previous, + stopping, + lifecycle_guard, + ) + .await + } + .instrument(request_span), + ) + .await + .map_err(|err| { + Status::internal(format!( + "sandbox stop worker terminated unexpectedly: {err}" + )) + })? + } + + async fn complete_sandbox_stop( + &self, + sandbox_id: String, + sandbox_name: String, + previous: Sandbox, + stopping: Sandbox, + lifecycle_guard: SandboxLifecycleGuard, + ) -> Result { + let result = self + .driver + .call("driver.stop_sandbox", Some(&sandbox_id), |driver| { + let sandbox_id = sandbox_id.clone(); + let sandbox_name = sandbox_name.clone(); + async move { + driver + .stop_sandbox(Request::new(StopSandboxRequest { + sandbox_id, + sandbox_name, + })) + .await + } + }) + .await; + + match result { + Ok(_) => { + let _global_guard = self.lock_global_for_lifecycle(&lifecycle_guard).await; + let latest = self + .store + .get_message::(&sandbox_id) + .await + .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? + .ok_or_else(|| Status::not_found("sandbox not found"))?; + let phase = SandboxPhase::try_from(latest.phase()).unwrap_or(SandboxPhase::Unknown); + let stopped = if phase == SandboxPhase::Stopped { + latest + } else if phase == SandboxPhase::Stopping { + self.write_lifecycle_phase( + &latest, + SandboxPhase::Stopped, + "Stopped", + "Sandbox compute is stopped", + ) + .await? + } else { + return Err(Status::aborted( + "sandbox lifecycle changed while stop completed", + )); + }; + self.cleanup_stopped_sandbox_sessions(&stopped) + .await + .map_err(Status::internal)?; + self.sandbox_index.update_from_sandbox(&stopped); + self.sandbox_watch_bus.notify(&sandbox_id); + Ok(stopped) + } + Err(err) => { + self.recover_failed_lifecycle(&lifecycle_guard, &stopping, &previous, true) + .await; + Err(Status::new( + err.code(), + format!("stop sandbox failed: {}", err.message()), + )) + } + } + } + + pub(crate) async fn start_sandbox( + &self, + workspace: &str, + name: &str, + ) -> Result { + let candidate = self + .store + .get_message_by_name::(workspace, name) + .await + .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? + .ok_or_else(|| Status::not_found("sandbox not found"))?; + let sandbox_id = candidate.object_id().to_string(); + let sandbox_name = candidate.object_name().to_string(); + let lifecycle_guard = self.lifecycle_gates.lock_for(&sandbox_id).await; + let global_guard = self.lock_global_for_lifecycle(&lifecycle_guard).await; + let current = self + .store + .get_message::(&sandbox_id) + .await + .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? + .ok_or_else(|| Status::not_found("sandbox not found"))?; + if current.object_name() != sandbox_name { + return Err(Status::aborted( + "sandbox name changed while the start request was waiting; retry explicitly", + )); + } + + let phase = SandboxPhase::try_from(current.phase()).unwrap_or(SandboxPhase::Unknown); + if phase == SandboxPhase::Ready { + return Ok(current); + } + if !matches!(phase, SandboxPhase::Stopped | SandboxPhase::Starting) { + return Err(Status::failed_precondition(format!( + "sandbox must be Stopped to start (current phase: {phase:?})" + ))); + } + + let (previous, starting) = if phase == SandboxPhase::Starting { + // Acquiring the lifecycle gate proves that no local worker still + // owns this transition. Retry the idempotent driver operation. + (current.clone(), current) + } else { + let previous = current.clone(); + let starting = self + .write_lifecycle_phase( + ¤t, + SandboxPhase::Starting, + "Starting", + "Sandbox start requested", + ) + .await?; + self.sandbox_index.update_from_sandbox(&starting); + self.sandbox_watch_bus.notify(&sandbox_id); + (previous, starting) + }; + drop(global_guard); + + // The durable `Starting` transition commits the operation. Let an + // owned worker finish it even if the initiating RPC is canceled. + let runtime = self.clone(); + let request_span = tracing::Span::current(); + tokio::spawn( + async move { + runtime + .complete_sandbox_start( + sandbox_id, + sandbox_name, + previous, + starting, + lifecycle_guard, + ) + .await + } + .instrument(request_span), + ) + .await + .map_err(|err| { + Status::internal(format!( + "sandbox start worker terminated unexpectedly: {err}" + )) + })? + } + + async fn complete_sandbox_start( + &self, + sandbox_id: String, + sandbox_name: String, + previous: Sandbox, + starting: Sandbox, + lifecycle_guard: SandboxLifecycleGuard, + ) -> Result { + let result = self + .driver + .call("driver.start_sandbox", Some(&sandbox_id), |driver| { + let sandbox_id = sandbox_id.clone(); + let sandbox_name = sandbox_name.clone(); + async move { + driver + .start_sandbox(Request::new(StartSandboxRequest { + sandbox_id, + sandbox_name, + })) + .await + } + }) + .await; + + match result { + Ok(_) => { + let _global_guard = self.lock_global_for_lifecycle(&lifecycle_guard).await; + let latest = self + .store + .get_message::(&sandbox_id) + .await + .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? + .ok_or_else(|| Status::not_found("sandbox not found"))?; + Ok(latest) + } + Err(err) => { + self.recover_failed_lifecycle(&lifecycle_guard, &starting, &previous, false) + .await; + Err(Status::new( + err.code(), + format!("start sandbox failed: {}", err.message()), + )) + } + } + } + + /// Reconcile an ambiguous lifecycle error against the driver's observed + /// state before deciding whether the pre-operation snapshot is still true. + /// + /// A transport error can arrive after the runtime applied stop or start. + /// The driver lookup deliberately runs without the process-wide lock; the + /// exact transition resource version then fences the recovery write. + async fn recover_failed_lifecycle( + &self, + lifecycle_guard: &SandboxLifecycleGuard, + transition: &Sandbox, + previous: &Sandbox, + expected_stopped: bool, + ) { + let sandbox_id = transition.object_id(); + let sandbox_name = transition.object_name(); + let observed = self.get_driver_sandbox(sandbox_id, sandbox_name).await; + let _global_guard = self.lock_global_for_lifecycle(lifecycle_guard).await; + + match observed { + Ok(Some(snapshot)) if snapshot.id == sandbox_id && snapshot.status.is_some() => { + let backend_phase = derive_phase(snapshot.status.as_ref()); + let observed_stopped = backend_phase == SandboxPhase::Stopped + || driver_snapshot_confirms_stopped(&snapshot); + let suspension_progressing = + expected_stopped && driver_snapshot_confirms_stopping(&snapshot); + if suspension_progressing { + // The Kubernetes controller has accepted the stop and + // is waiting for its pod to terminate. Preserve the + // durable transition so a later watch event can complete + // it instead of claiming the sandbox is running again. + debug!(sandbox_id, "Sandbox stop is still progressing"); + } else if backend_phase == SandboxPhase::Error + || observed_stopped == expected_stopped + { + if let Some(reconciled) = self + .reconcile_lifecycle_snapshot(transition, &snapshot) + .await + && reconciled.phase() == SandboxPhase::Stopped as i32 + && let Err(err) = self.cleanup_stopped_sandbox_sessions(&reconciled).await + { + warn!( + sandbox_id, + error = %err, + "Failed to clean up sessions after reconciling stopped sandbox" + ); + } + } else { + self.restore_lifecycle_snapshot(transition, previous).await; + } + } + Ok(Some(_) | None) | Err(_) => { + // Without authoritative backend state, retain the durable + // transition rather than claiming the old running/stopped + // state. Startup recovery can safely retry the idempotent + // driver operation. + warn!( + sandbox_id, + "Could not resolve ambiguous sandbox lifecycle outcome; retaining transition" + ); + } + } + } + + async fn reconcile_lifecycle_snapshot( + &self, + transition: &Sandbox, + snapshot: &DriverSandbox, + ) -> Option { + let sandbox_id = transition.object_id().to_string(); + let expected_resource_version = sandbox_resource_version(transition); + let session_connected = self.supervisor_sessions.has_session(&sandbox_id); + match self + .store + .update_message_cas::(&sandbox_id, expected_resource_version, |sandbox| { + apply_driver_snapshot(sandbox, snapshot, session_connected); + }) + .await + { + Ok(reconciled) => { + self.sandbox_index.update_from_sandbox(&reconciled); + self.sandbox_watch_bus.notify(&sandbox_id); + Some(reconciled) + } + Err(err) => { + debug!( + sandbox_id, + error = %err, + "Skipped lifecycle reconciliation after concurrent change" + ); + None + } + } + } + + async fn write_lifecycle_phase( + &self, + sandbox: &Sandbox, + phase: SandboxPhase, + reason: &str, + message: &str, + ) -> Result { + let sandbox_id = sandbox.object_id().to_string(); + let expected_resource_version = sandbox_resource_version(sandbox); + let reason = reason.to_string(); + let message = message.to_string(); + self.store + .update_message_cas::( + &sandbox_id, + expected_resource_version, + move |sandbox| { + sandbox.set_phase(phase as i32); + let name = sandbox.object_name().to_string(); + upsert_ready_condition( + &mut sandbox.status, + &name, + SandboxCondition { + r#type: "Ready".to_string(), + status: "False".to_string(), + reason: reason.clone(), + message: message.clone(), + last_transition_time: String::new(), + }, + ); + }, + ) + .await + .map_err(|e| crate::grpc::persistence_error_to_status(e, "update sandbox lifecycle")) + } + + async fn restore_lifecycle_snapshot(&self, owned: &Sandbox, previous: &Sandbox) { + let sandbox_id = owned.object_id().to_string(); + let previous = previous.clone(); + match self + .store + .update_message_cas::( + &sandbox_id, + sandbox_resource_version(owned), + move |sandbox| *sandbox = previous.clone(), + ) + .await + { + Ok(restored) => { + self.sandbox_index.update_from_sandbox(&restored); + self.sandbox_watch_bus.notify(&sandbox_id); + } + Err(err) => { + debug!(sandbox_id, error = %err, "Skipped lifecycle rollback after concurrent change"); + } + } + } + pub(crate) async fn delete_sandbox( &self, workspace: &str, @@ -999,8 +1443,8 @@ impl ComputeRuntime { sandbox_id: candidate.object_id().to_string(), sandbox_name: candidate.object_name().to_string(), }; - let delete_guard = self.delete_gates.lock_for(&target.sandbox_id).await; - let global_guard = self.lock_global_for_delete(&delete_guard).await; + let delete_guard = self.lifecycle_gates.lock_for(&target.sandbox_id).await; + let global_guard = self.lock_global_for_lifecycle(&delete_guard).await; // There is no await between acquiring the initial guards and spawning // the worker. From this commitment point onward, request cancellation @@ -1028,7 +1472,7 @@ impl ComputeRuntime { async fn delete_sandbox_inner( &self, target: SandboxDeleteTarget, - delete_guard: SandboxDeleteGuard, + delete_guard: SandboxLifecycleGuard, guard: tokio::sync::OwnedMutexGuard<()>, ) -> Result { let current = self @@ -1194,10 +1638,10 @@ impl ComputeRuntime { /// row leaves `Deleting`; that state belongs to a concurrent writer. async fn remove_deleting_sandbox_record( &self, - delete_guard: &SandboxDeleteGuard, + delete_guard: &SandboxLifecycleGuard, sandbox_id: &str, ) -> bool { - let _guard = self.lock_global_for_delete(delete_guard).await; + let _guard = self.lock_global_for_lifecycle(delete_guard).await; for attempt in 1..=DELETE_PHASE_CAS_RETRY_LIMIT { let record = match self.store.get(Sandbox::object_type(), sandbox_id).await { Ok(Some(record)) => record, @@ -1328,7 +1772,7 @@ impl ComputeRuntime { /// backend, or restore the pre-delete snapshot when lookup is inconclusive. async fn recover_failed_delete( &self, - delete_guard: &SandboxDeleteGuard, + delete_guard: &SandboxLifecycleGuard, transition: &DeleteTransition, ) { let sandbox_id = transition.deleting.object_id(); @@ -1337,7 +1781,7 @@ impl ComputeRuntime { // The driver lookup is deliberately outside the process-wide guard. let observed = self.get_driver_sandbox(sandbox_id, sandbox_name).await; - let _guard = self.lock_global_for_delete(delete_guard).await; + let _guard = self.lock_global_for_lifecycle(delete_guard).await; match observed { Ok(Some(snapshot)) if snapshot.id == sandbox_id && snapshot.status.is_some() => { @@ -1587,19 +2031,20 @@ impl ComputeRuntime { Ok(()) } - /// Resume sandboxes whose store records say they should be running. + /// Start sandboxes whose store records say they should be running. /// Drivers that do not auto-restart compute resources across gateway - /// restarts (currently only Docker) implement `StartupResume`. For + /// restarts (currently only Docker) implement `StartupSandboxStarter`. For /// each sandbox in the store whose phase is not `Deleting` or - /// `Error`, we ask the driver to resume the underlying resource. If + /// `Error`, we ask the driver to start the underlying resource. If /// the driver reports that the resource no longer exists or fails to /// start, the sandbox is moved to the `Error` phase so the failure /// surfaces in the UI. /// /// Should be called once at gateway startup, before watchers spawn, - /// so the watch loop sees the post-resume state on its first poll. - pub async fn resume_persisted_sandboxes(&self) -> Result<(), String> { - let Some(resume) = &self.startup_resume else { + /// so the watch loop sees the post-start state on its first poll. + pub async fn start_persisted_sandboxes(&self) -> Result<(), String> { + self.recover_persisted_lifecycle_transitions().await?; + let Some(startup_hook) = &self.startup_starter else { return Ok(()); }; @@ -1609,7 +2054,7 @@ impl ComputeRuntime { .await .map_err(|e| e.to_string())?; - let mut resumed = 0usize; + let mut started = 0usize; let mut missing = 0usize; let mut failed = 0usize; @@ -1617,7 +2062,7 @@ impl ComputeRuntime { let sandbox = match Sandbox::decode(record.payload.as_slice()) { Ok(sandbox) => sandbox, Err(err) => { - warn!(error = %err, "Failed to decode sandbox record during startup resume"); + warn!(error = %err, "Failed to decode sandbox record during gateway startup"); continue; } }; @@ -1627,8 +2072,8 @@ impl ComputeRuntime { continue; } - match resume - .resume_sandbox(sandbox.object_id(), sandbox.object_name()) + match startup_hook + .start_sandbox(sandbox.object_id(), sandbox.object_name()) .await { Ok(true) => { @@ -1636,9 +2081,9 @@ impl ComputeRuntime { sandbox_id = %sandbox.object_id(), sandbox_name = %sandbox.object_name(), ?phase, - "Resumed sandbox during gateway startup" + "Started sandbox during gateway startup" ); - resumed += 1; + started += 1; } Ok(false) => { // Backend resource is gone but the store still @@ -1649,7 +2094,7 @@ impl ComputeRuntime { warn!( sandbox_id = %sandbox.object_id(), sandbox_name = %sandbox.object_name(), - "Cannot resume sandbox: backend resource is missing" + "Cannot start sandbox: backend resource is missing" ); self.mark_sandbox_error( &sandbox, @@ -1664,12 +2109,12 @@ impl ComputeRuntime { sandbox_id = %sandbox.object_id(), sandbox_name = %sandbox.object_name(), error = %err, - "Failed to resume sandbox during gateway startup" + "Failed to start sandbox during gateway startup" ); self.mark_sandbox_error( &sandbox, - "ResumeFailed", - &format!("Failed to resume sandbox during gateway startup: {err}"), + "StartFailed", + &format!("Failed to start sandbox during gateway startup: {err}"), ) .await; failed += 1; @@ -1677,50 +2122,147 @@ impl ComputeRuntime { } } - if resumed > 0 || missing > 0 || failed > 0 { + if started > 0 || missing > 0 || failed > 0 { info!( - resumed, + started, missing_backend = missing, failed, - "Sandbox resume sweep complete" + "Sandbox start sweep complete" ); } Ok(()) } - async fn mark_sandbox_error(&self, sandbox: &Sandbox, reason: &str, message: &str) { - let _guard = self.sync_lock.lock().await; - let sandbox_id = sandbox.object_id().to_string(); - let reason = reason.to_string(); - let message = message.to_string(); - match self + async fn recover_persisted_lifecycle_transitions(&self) -> Result<(), String> { + let records = self .store - .update_message_cas::(&sandbox_id, 0, |s| { - s.set_phase(SandboxPhase::Error as i32); - let name = s.object_name().to_string(); - upsert_ready_condition( - &mut s.status, - &name, - SandboxCondition { - r#type: "Ready".to_string(), - status: "False".to_string(), - reason: reason.clone(), - message: message.clone(), - last_transition_time: String::new(), - }, - ); - }) + .list_by_type(Sandbox::object_type(), 1000, 0) .await - { - Ok(updated) => { - self.sandbox_index.update_from_sandbox(&updated); + .map_err(|e| e.to_string())?; + for record in records { + let sandbox = match Sandbox::decode(record.payload.as_slice()) { + Ok(sandbox) => sandbox, + Err(err) => { + warn!(error = %err, "Failed to decode sandbox during lifecycle recovery"); + continue; + } + }; + let phase = SandboxPhase::try_from(sandbox.phase()).unwrap_or(SandboxPhase::Unknown); + match phase { + SandboxPhase::Stopped => { + if let Err(err) = self.cleanup_stopped_sandbox_sessions(&sandbox).await { + warn!(sandbox_id = %sandbox.object_id(), error = %err, "Failed to complete recovered sandbox session cleanup"); + } + } + SandboxPhase::Stopping => { + let sandbox_id = sandbox.object_id().to_string(); + let sandbox_name = sandbox.object_name().to_string(); + let driver_sandbox_id = sandbox_id.clone(); + match self + .driver + .call( + "driver.stop_sandbox", + Some(&sandbox_id), + |driver| async move { + driver + .stop_sandbox(Request::new(StopSandboxRequest { + sandbox_id: driver_sandbox_id, + sandbox_name, + })) + .await + }, + ) + .await + { + Ok(_) => match self + .write_lifecycle_phase( + &sandbox, + SandboxPhase::Stopped, + "Stopped", + "Sandbox compute is stopped", + ) + .await + { + Ok(updated) => { + self.sandbox_index.update_from_sandbox(&updated); + self.sandbox_watch_bus.notify(updated.object_id()); + if let Err(err) = + self.cleanup_stopped_sandbox_sessions(&updated).await + { + warn!(sandbox_id = %updated.object_id(), error = %err, "Failed to complete recovered sandbox session cleanup"); + } + } + Err(err) => { + warn!(sandbox_id = %sandbox.object_id(), error = %err, "Failed to persist recovered stop"); + } + }, + Err(err) => { + warn!(sandbox_id = %sandbox.object_id(), error = %err, "Failed to recover sandbox stop"); + } + } + } + SandboxPhase::Starting => { + let sandbox_id = sandbox.object_id().to_string(); + let sandbox_name = sandbox.object_name().to_string(); + let driver_sandbox_id = sandbox_id.clone(); + if let Err(err) = self + .driver + .call( + "driver.start_sandbox", + Some(&sandbox_id), + |driver| async move { + driver + .start_sandbox(Request::new(StartSandboxRequest { + sandbox_id: driver_sandbox_id, + sandbox_name, + })) + .await + }, + ) + .await + { + warn!(sandbox_id = %sandbox.object_id(), error = %err, "Failed to recover sandbox start"); + } + } + _ => {} + } + } + Ok(()) + } + + async fn mark_sandbox_error(&self, sandbox: &Sandbox, reason: &str, message: &str) { + let _guard = self.sync_lock.lock().await; + let sandbox_id = sandbox.object_id().to_string(); + let reason = reason.to_string(); + let message = message.to_string(); + match self + .store + .update_message_cas::(&sandbox_id, 0, |s| { + s.set_phase(SandboxPhase::Error as i32); + let name = s.object_name().to_string(); + upsert_ready_condition( + &mut s.status, + &name, + SandboxCondition { + r#type: "Ready".to_string(), + status: "False".to_string(), + reason: reason.clone(), + message: message.clone(), + last_transition_time: String::new(), + }, + ); + }) + .await + { + Ok(updated) => { + self.sandbox_index.update_from_sandbox(&updated); self.sandbox_watch_bus.notify(&sandbox_id); } Err(err) => { warn!( sandbox_id = %sandbox_id, error = %err, - "Failed to persist sandbox error state during startup resume" + "Failed to persist sandbox error state during gateway startup" ); } } @@ -2058,7 +2600,9 @@ impl ComputeRuntime { return Ok(()); } - self.update_sandbox_record(incoming, existing_record.resource_version) + let existing_phase = + SandboxPhase::try_from(existing.phase()).unwrap_or(SandboxPhase::Unknown); + self.update_sandbox_record(incoming, existing_record.resource_version, existing_phase) .await } @@ -2068,6 +2612,7 @@ impl ComputeRuntime { &self, incoming: DriverSandbox, expected_resource_version: u64, + existing_phase: SandboxPhase, ) -> Result<(), String> { let session_connected = self.supervisor_sessions.has_session(&incoming.id); let sandbox = self @@ -2091,6 +2636,11 @@ impl ComputeRuntime { self.sandbox_index.update_from_sandbox(&sandbox); self.sandbox_watch_bus.notify(sandbox.object_id()); + if existing_phase != SandboxPhase::Stopped + && sandbox.phase() == SandboxPhase::Stopped as i32 + { + self.cleanup_stopped_sandbox_sessions(&sandbox).await?; + } Ok(()) } @@ -2119,7 +2669,13 @@ impl ComputeRuntime { }; let current_phase = SandboxPhase::try_from(existing.phase()).unwrap_or(SandboxPhase::Unknown); - if current_phase == SandboxPhase::Deleting || current_phase == SandboxPhase::Error { + if matches!( + current_phase, + SandboxPhase::Deleting + | SandboxPhase::Error + | SandboxPhase::Stopping + | SandboxPhase::Stopped + ) { return Ok(()); } if !connected && current_phase != SandboxPhase::Ready { @@ -2255,6 +2811,16 @@ impl ComputeRuntime { Ok(()) } + async fn cleanup_stopped_sandbox_sessions(&self, sandbox: &Sandbox) -> Result<(), String> { + // Disconnect first so a store failure cannot leave the stopped + // sandbox reachable through an existing supervisor stream. Both + // operations are idempotent and are retried for durable Stopped + // records during explicit stop requests and startup recovery. + self.supervisor_sessions.disconnect(sandbox.object_id()); + self.cleanup_sandbox_ssh_sessions(sandbox.object_id(), sandbox.object_workspace()) + .await + } + // TODO: introduce a per-sandbox cap on service endpoints and paginate // this cleanup loop, or query by sandbox label instead of scanning the // full workspace. Without a cap the flat 1,000-record page could miss @@ -2288,10 +2854,10 @@ impl ComputeRuntime { async fn cleanup_local_state_if_sandbox_absent( &self, - delete_guard: &SandboxDeleteGuard, + delete_guard: &SandboxLifecycleGuard, sandbox_id: &str, ) -> Result<(), Status> { - let _guard = self.lock_global_for_delete(delete_guard).await; + let _guard = self.lock_global_for_lifecycle(delete_guard).await; let record = self .store .get(Sandbox::object_type(), sandbox_id) @@ -2423,6 +2989,45 @@ impl ComputeRuntime { } let sandbox = decode_sandbox_record(¤t_record)?; + let phase = SandboxPhase::try_from(sandbox.phase()).unwrap_or(SandboxPhase::Unknown); + if matches!( + phase, + SandboxPhase::Stopping | SandboxPhase::Stopped | SandboxPhase::Starting + ) { + let updated = self + .store + .update_message_cas::( + &sandbox_id, + expected_resource_version, + |sandbox| { + sandbox.set_phase(SandboxPhase::Error as i32); + let name = sandbox.object_name().to_string(); + upsert_ready_condition( + &mut sandbox.status, + &name, + SandboxCondition { + r#type: "Ready".to_string(), + status: "False".to_string(), + reason: "ComputeResourceMissing".to_string(), + message: "The compute driver could not find the retained sandbox resource; delete the sandbox to clean up its remaining state" + .to_string(), + last_transition_time: String::new(), + }, + ); + }, + ) + .await + .map_err(|err| err.to_string())?; + warn!( + sandbox_id = %sandbox_id, + sandbox_name = %sandbox_name, + phase = ?phase, + "Retained sandbox resource disappeared from the compute driver" + ); + self.sandbox_index.update_from_sandbox(&updated); + self.sandbox_watch_bus.notify(&sandbox_id); + return Ok(()); + } info!( sandbox_id = %sandbox_id, sandbox_name = %sandbox_name, @@ -2829,7 +3434,7 @@ fn apply_driver_snapshot(sandbox: &mut Sandbox, incoming: &DriverSandbox, sessio let sandbox_name = &incoming.name; let cpv = sandbox.current_policy_version(); - let (phase, mut status) = incoming.status.as_ref().map_or_else( + let (mut phase, mut status) = incoming.status.as_ref().map_or_else( || { let mut phase = old_phase; let supervisor_promoted = session_connected @@ -2857,6 +3462,27 @@ fn apply_driver_snapshot(sandbox: &mut Sandbox, incoming: &DriverSandbox, sessio }, ); + phase = match old_phase { + SandboxPhase::Stopping + if phase == SandboxPhase::Stopped || driver_snapshot_confirms_stopped(incoming) => + { + SandboxPhase::Stopped + } + SandboxPhase::Stopping if driver_snapshot_confirms_stopping(incoming) => { + SandboxPhase::Stopping + } + SandboxPhase::Stopping if phase != SandboxPhase::Error => SandboxPhase::Stopping, + SandboxPhase::Stopped => SandboxPhase::Stopped, + SandboxPhase::Starting if !matches!(phase, SandboxPhase::Ready | SandboxPhase::Error) => { + SandboxPhase::Starting + } + _ => phase, + }; + + if let Some(status) = status.as_mut() { + status.phase = phase as i32; + } + if let Some(status) = status.as_mut() && status.sandbox_name.is_empty() { @@ -2900,6 +3526,31 @@ fn apply_driver_snapshot(sandbox: &mut Sandbox, incoming: &DriverSandbox, sessio sandbox.set_current_policy_version(cpv); } +fn driver_snapshot_confirms_stopped(incoming: &DriverSandbox) -> bool { + incoming.status.as_ref().is_some_and(|status| { + status.conditions.iter().any(|condition| { + condition.status.eq_ignore_ascii_case("false") + && matches!( + condition.reason.to_ascii_lowercase().as_str(), + "containerexited" | "containerstopped" + ) + }) + }) +} + +fn driver_snapshot_confirms_stopping(incoming: &DriverSandbox) -> bool { + incoming.status.as_ref().is_some_and(|status| { + status.conditions.iter().any(|condition| { + condition.r#type.eq_ignore_ascii_case("Suspended") + && condition.status.eq_ignore_ascii_case("false") + && matches!( + condition.reason.to_ascii_lowercase().as_str(), + "podterminating" | "podnotterminated" + ) + }) + }) +} + fn ensure_supervisor_ready_status(status: &mut Option, sandbox_name: &str) { upsert_ready_condition( status, @@ -2934,7 +3585,7 @@ impl ComposedPhase { // before this driver snapshot arrived. Keep Ready rather than letting a lagging // backend phase overwrite it. let phase = match backend_phase { - SandboxPhase::Error | SandboxPhase::Deleting => backend_phase, + SandboxPhase::Error | SandboxPhase::Deleting | SandboxPhase::Stopped => backend_phase, _ if session_connected => SandboxPhase::Ready, _ => SandboxPhase::Provisioning, }; @@ -3037,6 +3688,13 @@ fn derive_phase(status: Option<&DriverSandboxStatus>) -> SandboxPhase { return SandboxPhase::Deleting; } + if status.conditions.iter().any(|condition| { + condition.r#type.eq_ignore_ascii_case("Suspended") + && condition.status.eq_ignore_ascii_case("true") + }) { + return SandboxPhase::Stopped; + } + for condition in &status.conditions { if condition.r#type == "Ready" { return if condition.status.eq_ignore_ascii_case("true") { @@ -3090,6 +3748,7 @@ fn sandbox_phase_should_be_running(phase: SandboxPhase) -> bool { SandboxPhase::Unspecified | SandboxPhase::Provisioning | SandboxPhase::Ready + | SandboxPhase::Starting | SandboxPhase::Unknown ) } @@ -3185,7 +3844,7 @@ impl ComputeDriver for NoopTestDriver { async fn stop_sandbox( &self, - _request: Request, + _request: Request, ) -> Result, Status> { Ok(tonic::Response::new( @@ -3193,6 +3852,16 @@ impl ComputeDriver for NoopTestDriver { )) } + async fn start_sandbox( + &self, + _request: Request, + ) -> Result, Status> + { + Ok(tonic::Response::new( + openshell_core::proto::compute::v1::StartSandboxResponse {}, + )) + } + async fn delete_sandbox( &self, _request: Request, @@ -3226,7 +3895,7 @@ pub async fn new_test_runtime_for_driver(store: Arc, driver_name: &str) - driver_version: "test".to_string(), }, shutdown_cleanup: None, - startup_resume: None, + startup_starter: None, driver_process: None, default_image: "openshell/sandbox:test".to_string(), store, @@ -3235,7 +3904,7 @@ pub async fn new_test_runtime_for_driver(store: Arc, driver_name: &str) - tracing_log_bus: TracingLogBus::new(), supervisor_sessions: Arc::new(SupervisorSessionRegistry::new()), sync_lock: Arc::new(Mutex::new(())), - delete_gates: Arc::new(DeleteGateRegistry::default()), + lifecycle_gates: Arc::new(LifecycleGateRegistry::default()), gateway_listener_requirements: Vec::new(), replica_id: "test-replica".to_string(), } @@ -3247,8 +3916,8 @@ mod tests { use futures::stream; use openshell_core::proto::compute::v1::{ CreateSandboxResponse, DeleteSandboxResponse, GetCapabilitiesResponse, GetSandboxRequest, - GetSandboxResponse, StopSandboxRequest, StopSandboxResponse, ValidateSandboxCreateResponse, - WatchSandboxesDeletedEvent, WatchSandboxesSandboxEvent, + GetSandboxResponse, StartSandboxResponse, StopSandboxRequest, StopSandboxResponse, + ValidateSandboxCreateResponse, WatchSandboxesDeletedEvent, WatchSandboxesSandboxEvent, }; use std::collections::HashMap; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -3464,6 +4133,13 @@ mod tests { Ok(tonic::Response::new(StopSandboxResponse {})) } + async fn start_sandbox( + &self, + _request: Request, + ) -> Result, Status> { + Ok(tonic::Response::new(StartSandboxResponse {})) + } + async fn delete_sandbox( &self, _request: Request, @@ -3494,6 +4170,12 @@ mod tests { Error(&'static str), } + #[derive(Clone)] + enum ControlledLifecycleOutcome { + Ok, + Error(&'static str), + } + struct ControlledDriver { watch_tx: mpsc::UnboundedSender>, watch_rx: TestMutex>>>, @@ -3503,6 +4185,18 @@ mod tests { delete_blocked: AtomicBool, delete_calls: AtomicUsize, delete_outcome: TestMutex, + stop_started: Notify, + stop_finished: Notify, + stop_release: Semaphore, + stop_blocked: AtomicBool, + stop_calls: AtomicUsize, + stop_outcome: TestMutex, + start_started: Notify, + start_finished: Notify, + start_release: Semaphore, + start_blocked: AtomicBool, + start_calls: AtomicUsize, + start_outcome: TestMutex, get_started: Notify, get_release: Semaphore, get_blocked: AtomicBool, @@ -3521,6 +4215,18 @@ mod tests { delete_blocked: AtomicBool::new(false), delete_calls: AtomicUsize::new(0), delete_outcome: TestMutex::new(ControlledDeleteOutcome::Ok(true)), + stop_started: Notify::new(), + stop_finished: Notify::new(), + stop_release: Semaphore::new(0), + stop_blocked: AtomicBool::new(false), + stop_calls: AtomicUsize::new(0), + stop_outcome: TestMutex::new(ControlledLifecycleOutcome::Ok), + start_started: Notify::new(), + start_finished: Notify::new(), + start_release: Semaphore::new(0), + start_blocked: AtomicBool::new(false), + start_calls: AtomicUsize::new(0), + start_outcome: TestMutex::new(ControlledLifecycleOutcome::Ok), get_started: Notify::new(), get_release: Semaphore::new(0), get_blocked: AtomicBool::new(false), @@ -3536,6 +4242,22 @@ mod tests { self.delete_release.add_permits(1); } + fn block_stop(&self) { + self.stop_blocked.store(true, Ordering::SeqCst); + } + + fn release_stop(&self) { + self.stop_release.add_permits(1); + } + + fn block_start(&self) { + self.start_blocked.store(true, Ordering::SeqCst); + } + + fn release_start(&self) { + self.start_release.add_permits(1); + } + fn block_get(&self) { self.get_blocked.store(true, Ordering::SeqCst); } @@ -3551,6 +4273,20 @@ mod tests { .expect("delete outcome lock poisoned") = outcome; } + fn set_stop_outcome(&self, outcome: ControlledLifecycleOutcome) { + *self + .stop_outcome + .lock() + .expect("stop outcome lock poisoned") = outcome; + } + + fn set_start_outcome(&self, outcome: ControlledLifecycleOutcome) { + *self + .start_outcome + .lock() + .expect("start outcome lock poisoned") = outcome; + } + fn set_get_outcome(&self, outcome: ControlledGetOutcome) { *self.get_outcome.lock().expect("get outcome lock poisoned") = outcome; } @@ -3559,6 +4295,14 @@ mod tests { self.delete_calls.load(Ordering::SeqCst) } + fn stop_calls(&self) -> usize { + self.stop_calls.load(Ordering::SeqCst) + } + + fn start_calls(&self) -> usize { + self.start_calls.load(Ordering::SeqCst) + } + fn send_event(&self, event: WatchSandboxesEvent) { self.watch_tx .send(Ok(event)) @@ -3650,7 +4394,50 @@ mod tests { &self, _request: Request, ) -> Result, Status> { - Ok(tonic::Response::new(StopSandboxResponse {})) + self.stop_calls.fetch_add(1, Ordering::SeqCst); + self.stop_started.notify_one(); + if self.stop_blocked.load(Ordering::SeqCst) { + self.stop_release + .acquire() + .await + .expect("stop release semaphore closed") + .forget(); + } + self.stop_finished.notify_one(); + let outcome = self + .stop_outcome + .lock() + .expect("stop outcome lock poisoned") + .clone(); + match outcome { + ControlledLifecycleOutcome::Ok => Ok(tonic::Response::new(StopSandboxResponse {})), + ControlledLifecycleOutcome::Error(message) => Err(Status::internal(message)), + } + } + + async fn start_sandbox( + &self, + _request: Request, + ) -> Result, Status> { + self.start_calls.fetch_add(1, Ordering::SeqCst); + self.start_started.notify_one(); + if self.start_blocked.load(Ordering::SeqCst) { + self.start_release + .acquire() + .await + .expect("start release semaphore closed") + .forget(); + } + self.start_finished.notify_one(); + let outcome = self + .start_outcome + .lock() + .expect("start outcome lock poisoned") + .clone(); + match outcome { + ControlledLifecycleOutcome::Ok => Ok(tonic::Response::new(StartSandboxResponse {})), + ControlledLifecycleOutcome::Error(message) => Err(Status::internal(message)), + } } async fn delete_sandbox( @@ -3697,12 +4484,12 @@ mod tests { } async fn test_runtime(driver: SharedComputeDriver) -> ComputeRuntime { - test_runtime_with_resume(driver, None).await + test_runtime_with_start(driver, None).await } - async fn test_runtime_with_resume( + async fn test_runtime_with_start( driver: SharedComputeDriver, - startup_resume: Option>, + startup_starter: Option>, ) -> ComputeRuntime { let store = Arc::new(Store::connect("sqlite::memory:").await.unwrap()); ComputeRuntime { @@ -3713,7 +4500,7 @@ mod tests { driver_version: "test".to_string(), }, shutdown_cleanup: None, - startup_resume, + startup_starter, driver_process: None, default_image: "openshell/sandbox:test".to_string(), store, @@ -3722,7 +4509,7 @@ mod tests { tracing_log_bus: TracingLogBus::new(), supervisor_sessions: Arc::new(SupervisorSessionRegistry::new()), sync_lock: Arc::new(Mutex::new(())), - delete_gates: Arc::new(DeleteGateRegistry::default()), + lifecycle_gates: Arc::new(LifecycleGateRegistry::default()), gateway_listener_requirements: Vec::new(), replica_id: "test-replica".to_string(), } @@ -4010,7 +4797,7 @@ mod tests { #[test] fn delete_gate_registry_removes_stale_entries() { - let registry = DeleteGateRegistry::default(); + let registry = LifecycleGateRegistry::default(); let first = registry.gate_for("sb-1"); assert_eq!(registry.entry_count(), 1); drop(first); @@ -4440,6 +5227,13 @@ mod tests { self.0.stop_sandbox(request).await } + async fn start_sandbox( + &self, + request: Request, + ) -> Result, Status> { + self.0.start_sandbox(request).await + } + async fn delete_sandbox( &self, request: Request, @@ -4485,6 +5279,539 @@ mod tests { ); } + #[tokio::test] + async fn stop_and_start_follow_durable_state_machine() { + let driver = ControlledDriver::new(); + let runtime = test_runtime(driver.clone()).await; + let sandbox = sandbox_record("sb-lifecycle", "sandbox-lifecycle", SandboxPhase::Ready); + runtime.store.put_message(&sandbox).await.unwrap(); + let session = ssh_session_record("lifecycle-session", sandbox.object_id()); + runtime.store.put_message(&session).await.unwrap(); + register_test_supervisor_session(&runtime, sandbox.object_id()); + + let stopped = runtime + .stop_sandbox("default", sandbox.object_name()) + .await + .unwrap(); + assert_eq!(stopped.phase(), SandboxPhase::Stopped as i32); + assert_eq!(driver.stop_calls(), 1); + assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .store + .get_message::(session.object_id()) + .await + .unwrap() + .is_none(), + "stop revokes ephemeral SSH sessions" + ); + + let stopped_again = runtime + .stop_sandbox("default", sandbox.object_name()) + .await + .unwrap(); + assert_eq!(stopped_again.phase(), SandboxPhase::Stopped as i32); + assert_eq!(driver.stop_calls(), 1, "stable stop is idempotent"); + + let starting = runtime + .start_sandbox("default", sandbox.object_name()) + .await + .unwrap(); + assert_eq!(starting.phase(), SandboxPhase::Starting as i32); + assert_eq!(driver.start_calls(), 1); + + let starting_again = runtime + .start_sandbox("default", sandbox.object_name()) + .await + .unwrap(); + assert_eq!(starting_again.phase(), SandboxPhase::Starting as i32); + assert_eq!( + driver.start_calls(), + 2, + "explicit retry reissues the idempotent start" + ); + + register_test_supervisor_session(&runtime, sandbox.object_id()); + runtime + .apply_sandbox_update(ready_driver_sandbox( + sandbox.object_id(), + sandbox.object_name(), + )) + .await + .unwrap(); + let ready = runtime + .start_sandbox("default", sandbox.object_name()) + .await + .unwrap(); + assert_eq!(ready.phase(), SandboxPhase::Ready as i32); + assert_eq!(driver.start_calls(), 2, "ready start is idempotent"); + } + + #[tokio::test] + async fn retained_stopping_transition_retries_driver_operation() { + let driver = ControlledDriver::new(); + let runtime = test_runtime(driver.clone()).await; + let sandbox = sandbox_record( + "sb-retained-stop", + "sandbox-retained-stop", + SandboxPhase::Stopping, + ); + runtime.store.put_message(&sandbox).await.unwrap(); + + let stopped = runtime + .stop_sandbox("default", sandbox.object_name()) + .await + .unwrap(); + + assert_eq!(stopped.phase(), SandboxPhase::Stopped as i32); + assert_eq!(driver.stop_calls(), 1); + } + + #[tokio::test] + async fn retained_starting_transition_retries_driver_operation() { + let driver = ControlledDriver::new(); + let runtime = test_runtime(driver.clone()).await; + let sandbox = sandbox_record( + "sb-retained-start", + "sandbox-retained-start", + SandboxPhase::Starting, + ); + runtime.store.put_message(&sandbox).await.unwrap(); + + let starting = runtime + .start_sandbox("default", sandbox.object_name()) + .await + .unwrap(); + + assert_eq!(starting.phase(), SandboxPhase::Starting as i32); + assert_eq!(driver.start_calls(), 1); + } + + #[tokio::test] + async fn repeated_stop_completes_session_cleanup() { + let driver = ControlledDriver::new(); + let runtime = test_runtime(driver.clone()).await; + let sandbox = sandbox_record("sb-stopped", "sandbox-stopped", SandboxPhase::Stopped); + let session = ssh_session_record("stale-session", sandbox.object_id()); + runtime.store.put_message(&sandbox).await.unwrap(); + runtime.store.put_message(&session).await.unwrap(); + register_test_supervisor_session(&runtime, sandbox.object_id()); + + let stopped = runtime + .stop_sandbox("default", sandbox.object_name()) + .await + .unwrap(); + + assert_eq!(stopped.phase(), SandboxPhase::Stopped as i32); + assert_eq!(driver.stop_calls(), 0); + assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .store + .get_message::(session.object_id()) + .await + .unwrap() + .is_none() + ); + } + + #[tokio::test] + async fn startup_recovery_completes_stopped_session_cleanup() { + let driver = ControlledDriver::new(); + let runtime = test_runtime(driver.clone()).await; + let stopped = sandbox_record("sb-stopped", "sandbox-stopped", SandboxPhase::Stopped); + let stopping = sandbox_record("sb-stopping", "sandbox-stopping", SandboxPhase::Stopping); + let stopped_session = ssh_session_record("stopped-session", stopped.object_id()); + let stopping_session = ssh_session_record("stopping-session", stopping.object_id()); + for sandbox in [&stopped, &stopping] { + runtime.store.put_message(sandbox).await.unwrap(); + register_test_supervisor_session(&runtime, sandbox.object_id()); + } + for session in [&stopped_session, &stopping_session] { + runtime.store.put_message(session).await.unwrap(); + } + + runtime.start_persisted_sandboxes().await.unwrap(); + + assert_eq!(driver.stop_calls(), 1); + for (sandbox, session) in [(&stopped, &stopped_session), (&stopping, &stopping_session)] { + let stored = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Stopped as i32); + assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .store + .get_message::(session.object_id()) + .await + .unwrap() + .is_none() + ); + } + } + + #[tokio::test] + async fn request_cancellation_does_not_cancel_stop_worker() { + let driver = ControlledDriver::new(); + driver.block_stop(); + let runtime = test_runtime(driver.clone()).await; + let sandbox = sandbox_record("sb-stop", "sandbox-stop", SandboxPhase::Ready); + runtime.store.put_message(&sandbox).await.unwrap(); + + let request_runtime = runtime.clone(); + let request = tokio::spawn(async move { + request_runtime + .stop_sandbox("default", "sandbox-stop") + .await + }); + tokio::time::timeout(Duration::from_secs(1), driver.stop_started.notified()) + .await + .expect("stop did not reach the driver"); + + request.abort(); + assert!(request.await.unwrap_err().is_cancelled()); + driver.release_stop(); + tokio::time::timeout(Duration::from_secs(1), driver.stop_finished.notified()) + .await + .expect("detached stop worker did not finish the driver call"); + + tokio::time::timeout(Duration::from_secs(1), async { + loop { + let stored = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + if stored.phase() == SandboxPhase::Stopped as i32 { + break; + } + tokio::task::yield_now().await; + } + }) + .await + .expect("detached stop worker did not persist Stopped"); + assert_eq!(driver.stop_calls(), 1); + } + + #[tokio::test] + async fn request_cancellation_does_not_cancel_start_worker() { + let driver = ControlledDriver::new(); + driver.block_start(); + let runtime = test_runtime(driver.clone()).await; + let sandbox = sandbox_record("sb-start", "sandbox-start", SandboxPhase::Stopped); + runtime.store.put_message(&sandbox).await.unwrap(); + + let request_runtime = runtime.clone(); + let request = tokio::spawn(async move { + request_runtime + .start_sandbox("default", "sandbox-start") + .await + }); + tokio::time::timeout(Duration::from_secs(1), driver.start_started.notified()) + .await + .expect("start did not reach the driver"); + + request.abort(); + assert!(request.await.unwrap_err().is_cancelled()); + driver.release_start(); + tokio::time::timeout(Duration::from_secs(1), driver.start_finished.notified()) + .await + .expect("detached start worker did not finish the driver call"); + + driver.release_start(); + let starting = tokio::time::timeout( + Duration::from_secs(1), + runtime.start_sandbox("default", sandbox.object_name()), + ) + .await + .expect("detached start worker did not release the lifecycle gate") + .unwrap(); + assert_eq!(starting.phase(), SandboxPhase::Starting as i32); + assert_eq!(driver.start_calls(), 2); + } + + #[tokio::test] + async fn failed_stop_reconciles_backend_that_already_stopped() { + let driver = ControlledDriver::new(); + driver.set_stop_outcome(ControlledLifecycleOutcome::Error("response lost")); + let sandbox = sandbox_record("sb-stop", "sandbox-stop", SandboxPhase::Ready); + let mut stopped = ready_driver_sandbox(sandbox.object_id(), sandbox.object_name()); + stopped.status = Some(make_driver_status(make_driver_condition( + "ContainerExited", + "container stopped before the response was lost", + ))); + driver.set_get_outcome(ControlledGetOutcome::Sandbox(Box::new(stopped))); + let runtime = test_runtime(driver).await; + runtime.store.put_message(&sandbox).await.unwrap(); + let session = ssh_session_record("lost-response-session", sandbox.object_id()); + runtime.store.put_message(&session).await.unwrap(); + register_test_supervisor_session(&runtime, sandbox.object_id()); + + let err = runtime + .stop_sandbox("default", sandbox.object_name()) + .await + .unwrap_err(); + assert!(err.message().contains("response lost")); + + let stored = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Stopped as i32); + assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .store + .get_message::(session.object_id()) + .await + .unwrap() + .is_none(), + "reconciled stop revokes ephemeral SSH sessions" + ); + } + + #[tokio::test] + async fn failed_stop_retains_progress_and_watcher_completes_cleanup() { + let driver = ControlledDriver::new(); + driver.set_stop_outcome(ControlledLifecycleOutcome::Error("stop timed out")); + let sandbox = sandbox_record( + "sb-stop-progressing", + "sandbox-stop-progressing", + SandboxPhase::Ready, + ); + let mut progressing = ready_driver_sandbox(sandbox.object_id(), sandbox.object_name()); + progressing.status = Some(DriverSandboxStatus { + sandbox_name: sandbox.object_name().to_string(), + instance_id: format!("{}-pod", sandbox.object_name()), + conditions: vec![ + DriverCondition { + r#type: "Suspended".to_string(), + status: "False".to_string(), + reason: "PodTerminating".to_string(), + message: "Pod is terminating. Sandbox is stopping".to_string(), + last_transition_time: String::new(), + }, + make_driver_condition("SandboxStopped", "Sandbox is stopping"), + ], + ..Default::default() + }); + driver.set_get_outcome(ControlledGetOutcome::Sandbox(Box::new(progressing.clone()))); + let runtime = test_runtime(driver).await; + runtime.store.put_message(&sandbox).await.unwrap(); + let session = ssh_session_record("progressing-session", sandbox.object_id()); + runtime.store.put_message(&session).await.unwrap(); + register_test_supervisor_session(&runtime, sandbox.object_id()); + + let err = runtime + .stop_sandbox("default", sandbox.object_name()) + .await + .unwrap_err(); + assert!(err.message().contains("stop timed out")); + + let stored = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Stopping as i32); + assert!(runtime.supervisor_sessions.has_session(sandbox.object_id())); + + progressing.status.as_mut().unwrap().conditions[0].status = "True".to_string(); + progressing.status.as_mut().unwrap().conditions[0].reason = "PodTerminated".to_string(); + runtime.apply_sandbox_update(progressing).await.unwrap(); + + let stored = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Stopped as i32); + assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .store + .get_message::(session.object_id()) + .await + .unwrap() + .is_none(), + "watcher-driven stop revokes ephemeral SSH sessions" + ); + } + + #[tokio::test] + async fn failed_start_reconciles_backend_that_already_started() { + let driver = ControlledDriver::new(); + driver.set_start_outcome(ControlledLifecycleOutcome::Error("response lost")); + let sandbox = sandbox_record("sb-start", "sandbox-start", SandboxPhase::Stopped); + driver.set_get_outcome(ControlledGetOutcome::Sandbox(Box::new( + ready_driver_sandbox(sandbox.object_id(), sandbox.object_name()), + ))); + let runtime = test_runtime(driver).await; + runtime.store.put_message(&sandbox).await.unwrap(); + + let err = runtime + .start_sandbox("default", sandbox.object_name()) + .await + .unwrap_err(); + assert!(err.message().contains("response lost")); + + let stored = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.phase(), SandboxPhase::Starting as i32); + } + + #[tokio::test] + async fn lifecycle_operations_reject_invalid_source_phases() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let sandbox = sandbox_record( + "sb-provisioning", + "sandbox-provisioning", + SandboxPhase::Provisioning, + ); + runtime.store.put_message(&sandbox).await.unwrap(); + + let stop = runtime + .stop_sandbox("default", sandbox.object_name()) + .await + .unwrap_err(); + assert_eq!(stop.code(), Code::FailedPrecondition); + + let start = runtime + .start_sandbox("default", sandbox.object_name()) + .await + .unwrap_err(); + assert_eq!(start.code(), Code::FailedPrecondition); + } + + #[tokio::test] + async fn stale_ready_snapshot_cannot_wake_stopped_sandbox() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let sandbox = sandbox_record("sb-sleeping", "sandbox-sleeping", SandboxPhase::Stopped); + runtime.store.put_message(&sandbox).await.unwrap(); + register_test_supervisor_session(&runtime, sandbox.object_id()); + + runtime + .apply_sandbox_update(ready_driver_sandbox( + sandbox.object_id(), + sandbox.object_name(), + )) + .await + .unwrap(); + + let current = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + assert_eq!(current.phase(), SandboxPhase::Stopped as i32); + } + + #[tokio::test] + async fn repeated_stopped_snapshot_does_not_repeat_session_cleanup() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let sandbox = sandbox_record("sb-stopping", "sandbox-stopping", SandboxPhase::Stopping); + runtime.store.put_message(&sandbox).await.unwrap(); + let transition_session = ssh_session_record("transition-session", sandbox.object_id()); + runtime + .store + .put_message(&transition_session) + .await + .unwrap(); + register_test_supervisor_session(&runtime, sandbox.object_id()); + + let mut stopped = ready_driver_sandbox(sandbox.object_id(), sandbox.object_name()); + stopped.status = Some(make_driver_status(make_driver_condition( + "ContainerExited", + "container stopped by request", + ))); + runtime.apply_sandbox_update(stopped.clone()).await.unwrap(); + + assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .store + .get_message::(transition_session.object_id()) + .await + .unwrap() + .is_none(), + "the transition to stopped cleans up ephemeral SSH sessions" + ); + + let later_session = ssh_session_record("later-session", sandbox.object_id()); + runtime.store.put_message(&later_session).await.unwrap(); + register_test_supervisor_session(&runtime, sandbox.object_id()); + runtime.apply_sandbox_update(stopped).await.unwrap(); + + assert!(runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .store + .get_message::(later_session.object_id()) + .await + .unwrap() + .is_some(), + "later stopped snapshots do not repeat session cleanup" + ); + } + + #[tokio::test] + async fn stopped_container_snapshot_confirms_stopping_sandbox() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let sandbox = sandbox_record("sb-stopping", "sandbox-stopping", SandboxPhase::Stopping); + runtime.store.put_message(&sandbox).await.unwrap(); + let mut stopped = ready_driver_sandbox(sandbox.object_id(), sandbox.object_name()); + stopped.status = Some(make_driver_status(make_driver_condition( + "ContainerExited", + "container stopped by request", + ))); + + runtime.apply_sandbox_update(stopped).await.unwrap(); + + let current = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + assert_eq!(current.phase(), SandboxPhase::Stopped as i32); + } + + #[tokio::test] + async fn stopped_container_snapshot_cannot_error_stopped_sandbox() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let sandbox = sandbox_record("sb-stopped", "sandbox-stopped", SandboxPhase::Stopped); + runtime.store.put_message(&sandbox).await.unwrap(); + let mut stopped = ready_driver_sandbox(sandbox.object_id(), sandbox.object_name()); + stopped.status = Some(make_driver_status(make_driver_condition( + "ContainerExited", + "container is stopped", + ))); + + runtime.apply_sandbox_update(stopped).await.unwrap(); + + let current = runtime + .store + .get_message::(sandbox.object_id()) + .await + .unwrap() + .unwrap(); + assert_eq!(current.phase(), SandboxPhase::Stopped as i32); + } + #[tokio::test] async fn begin_sandbox_delete_retries_after_stale_snapshot_conflict() { let runtime = test_runtime(Arc::new(TestDriver::default())).await; @@ -4832,7 +6159,7 @@ mod tests { let sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Ready); runtime.store.put_message(&sandbox).await.unwrap(); - let delete_gate = runtime.delete_gates.gate_for(sandbox.object_id()); + let delete_gate = runtime.lifecycle_gates.gate_for(sandbox.object_id()); let first_runtime = runtime.clone(); let first = tokio::spawn(async move { first_runtime.delete_sandbox("default", "sandbox-a").await }); @@ -4896,7 +6223,7 @@ mod tests { let sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Ready); runtime.store.put_message(&sandbox).await.unwrap(); - let delete_gate = runtime.delete_gates.gate_for(sandbox.object_id()); + let delete_gate = runtime.lifecycle_gates.gate_for(sandbox.object_id()); let first_runtime = runtime.clone(); let first = tokio::spawn(async move { first_runtime.delete_sandbox("default", "sandbox-a").await }); @@ -4955,7 +6282,7 @@ mod tests { // Hold the original ID's gate so the request resolves the name and // then waits before it can revalidate the durable row. - let delete_gate = runtime.delete_gates.gate_for(original.object_id()); + let delete_gate = runtime.lifecycle_gates.gate_for(original.object_id()); let delete_guard = delete_gate.lock().await; let delete_runtime = runtime.clone(); let delete = @@ -6550,12 +7877,12 @@ mod tests { } #[derive(Default)] - struct RecordingResume { + struct RecordingStart { calls: Mutex>, results: Mutex>>, } - impl RecordingResume { + impl RecordingStart { async fn set_result(&self, sandbox_id: &str, result: Result) { self.results .lock() @@ -6569,8 +7896,8 @@ mod tests { } #[tonic::async_trait] - impl StartupResume for RecordingResume { - async fn resume_sandbox( + impl StartupSandboxStarter for RecordingStart { + async fn start_sandbox( &self, sandbox_id: &str, sandbox_name: &str, @@ -6589,10 +7916,10 @@ mod tests { } #[tokio::test] - async fn resume_persisted_sandboxes_resumes_running_phases() { - let resume = Arc::new(RecordingResume::default()); + async fn start_persisted_sandboxes_starts_running_phases() { + let start = Arc::new(RecordingStart::default()); let runtime = - test_runtime_with_resume(Arc::new(TestDriver::default()), Some(resume.clone())).await; + test_runtime_with_start(Arc::new(TestDriver::default()), Some(start.clone())).await; for (id, name, phase) in [ ("sb-unspecified", "unspecified", SandboxPhase::Unspecified), @@ -6606,9 +7933,9 @@ mod tests { runtime.store.put_message(&sandbox).await.unwrap(); } - runtime.resume_persisted_sandboxes().await.unwrap(); + runtime.start_persisted_sandboxes().await.unwrap(); - let mut called_ids = resume + let mut called_ids = start .calls() .await .into_iter() @@ -6627,16 +7954,16 @@ mod tests { } #[tokio::test] - async fn resume_persisted_sandboxes_marks_missing_backend_as_error() { - let resume = Arc::new(RecordingResume::default()); - resume.set_result("sb-1", Ok(false)).await; + async fn start_persisted_sandboxes_marks_missing_backend_as_error() { + let start = Arc::new(RecordingStart::default()); + start.set_result("sb-1", Ok(false)).await; let runtime = - test_runtime_with_resume(Arc::new(TestDriver::default()), Some(resume.clone())).await; + test_runtime_with_start(Arc::new(TestDriver::default()), Some(start.clone())).await; let sandbox = sandbox_record("sb-1", "missing", SandboxPhase::Ready); runtime.store.put_message(&sandbox).await.unwrap(); - runtime.resume_persisted_sandboxes().await.unwrap(); + runtime.start_persisted_sandboxes().await.unwrap(); let stored = runtime .store @@ -6657,18 +7984,18 @@ mod tests { } #[tokio::test] - async fn resume_persisted_sandboxes_marks_failed_resume_as_error() { - let resume = Arc::new(RecordingResume::default()); - resume + async fn start_persisted_sandboxes_marks_failed_start_as_error() { + let start = Arc::new(RecordingStart::default()); + start .set_result("sb-1", Err("docker daemon angry".to_string())) .await; let runtime = - test_runtime_with_resume(Arc::new(TestDriver::default()), Some(resume.clone())).await; + test_runtime_with_start(Arc::new(TestDriver::default()), Some(start.clone())).await; let sandbox = sandbox_record("sb-1", "broken", SandboxPhase::Provisioning); runtime.store.put_message(&sandbox).await.unwrap(); - runtime.resume_persisted_sandboxes().await.unwrap(); + runtime.start_persisted_sandboxes().await.unwrap(); let stored = runtime .store @@ -6685,17 +8012,17 @@ mod tests { .as_ref() .and_then(|s| s.conditions.iter().find(|c| c.r#type == "Ready")) .expect("Ready condition present"); - assert_eq!(ready.reason, "ResumeFailed"); + assert_eq!(ready.reason, "StartFailed"); assert!(ready.message.contains("docker daemon angry")); } #[tokio::test] - async fn resume_persisted_sandboxes_is_noop_without_resume_hook() { + async fn start_persisted_sandboxes_is_noop_without_start_hook() { let runtime = test_runtime(Arc::new(TestDriver::default())).await; let sandbox = sandbox_record("sb-1", "anywhere", SandboxPhase::Ready); runtime.store.put_message(&sandbox).await.unwrap(); - runtime.resume_persisted_sandboxes().await.unwrap(); + runtime.start_persisted_sandboxes().await.unwrap(); let stored = runtime .store diff --git a/crates/openshell-server/src/grpc/mod.rs b/crates/openshell-server/src/grpc/mod.rs index d84ca41557..a0ea195442 100644 --- a/crates/openshell-server/src/grpc/mod.rs +++ b/crates/openshell-server/src/grpc/mod.rs @@ -46,8 +46,9 @@ use openshell_core::proto::{ RemoveWorkspaceMemberResponse, ReportPolicyStatusRequest, ReportPolicyStatusResponse, RevokeSshSessionRequest, RevokeSshSessionResponse, RotateProviderCredentialRequest, RotateProviderCredentialResponse, SandboxResponse, ServiceEndpointResponse, ServiceStatus, - SubmitPolicyAnalysisRequest, SubmitPolicyAnalysisResponse, SupervisorMessage, TcpForwardFrame, - UndoDraftChunkRequest, UndoDraftChunkResponse, UpdateConfigRequest, UpdateConfigResponse, + StartSandboxRequest, StopSandboxRequest, SubmitPolicyAnalysisRequest, + SubmitPolicyAnalysisResponse, SupervisorMessage, TcpForwardFrame, UndoDraftChunkRequest, + UndoDraftChunkResponse, UpdateConfigRequest, UpdateConfigResponse, UpdateProviderProfilesRequest, UpdateProviderProfilesResponse, UpdateProviderRequest, WatchSandboxRequest, open_shell_server::OpenShell, }; @@ -323,6 +324,20 @@ impl OpenShell for OpenShellService { sandbox::handle_delete_sandbox(&self.state, request).await } + async fn stop_sandbox( + &self, + request: Request, + ) -> Result, Status> { + sandbox::handle_stop_sandbox(&self.state, request).await + } + + async fn start_sandbox( + &self, + request: Request, + ) -> Result, Status> { + sandbox::handle_start_sandbox(&self.state, request).await + } + // --- Exec --- type ExecSandboxStream = ReceiverStream>; diff --git a/crates/openshell-server/src/grpc/sandbox.rs b/crates/openshell-server/src/grpc/sandbox.rs index 504532d5eb..9338956950 100644 --- a/crates/openshell-server/src/grpc/sandbox.rs +++ b/crates/openshell-server/src/grpc/sandbox.rs @@ -23,8 +23,9 @@ use openshell_core::proto::{ ExecSandboxInput, ExecSandboxRequest, ExecSandboxStderr, ExecSandboxStdout, GetSandboxRequest, ListSandboxProvidersRequest, ListSandboxProvidersResponse, ListSandboxesRequest, ListSandboxesResponse, Provider, RevokeSshSessionRequest, RevokeSshSessionResponse, - SandboxResponse, SandboxStreamEvent, SshRelayTarget, TcpForwardFrame, TcpForwardInit, - TcpRelayTarget, WatchSandboxRequest, relay_open, tcp_forward_init, + SandboxResponse, SandboxStreamEvent, SshRelayTarget, StartSandboxRequest, StopSandboxRequest, + TcpForwardFrame, TcpForwardInit, TcpRelayTarget, WatchSandboxRequest, relay_open, + tcp_forward_init, }; use openshell_core::proto::{Sandbox, SandboxPhase, SandboxTemplate, SshSession}; use openshell_core::telemetry::{ @@ -763,6 +764,94 @@ async fn handle_delete_sandbox_inner( })) } +pub(super) async fn handle_stop_sandbox( + state: &Arc, + request: Request, +) -> Result, Status> { + let result = handle_stop_sandbox_inner(state, request).await; + openshell_core::telemetry::emit_lifecycle( + LifecycleResource::Sandbox, + LifecycleOperation::Stop, + if result.is_ok() { + TelemetryOutcome::Success + } else { + TelemetryOutcome::Failure + }, + ); + result +} + +async fn handle_stop_sandbox_inner( + state: &Arc, + request: Request, +) -> Result, Status> { + let principal = super::extract_principal(&request)?; + let req = request.into_inner(); + if req.name.is_empty() { + return Err(Status::invalid_argument("name is required")); + } + let authz = authorize_workspace( + &state.store, + &state.admin_role, + &principal, + &req.workspace, + MinWorkspaceRole::User, + ) + .await?; + let workspace = super::workspace::resolve_workspace(state.store.as_ref(), &authz.workspace) + .await? + .name; + let sandbox = state.compute.stop_sandbox(&workspace, &req.name).await?; + info!(sandbox_name = %req.name, "StopSandbox request completed successfully"); + Ok(Response::new(SandboxResponse { + sandbox: Some(sandbox), + })) +} + +pub(super) async fn handle_start_sandbox( + state: &Arc, + request: Request, +) -> Result, Status> { + let result = handle_start_sandbox_inner(state, request).await; + openshell_core::telemetry::emit_lifecycle( + LifecycleResource::Sandbox, + LifecycleOperation::Start, + if result.is_ok() { + TelemetryOutcome::Success + } else { + TelemetryOutcome::Failure + }, + ); + result +} + +async fn handle_start_sandbox_inner( + state: &Arc, + request: Request, +) -> Result, Status> { + let principal = super::extract_principal(&request)?; + let req = request.into_inner(); + if req.name.is_empty() { + return Err(Status::invalid_argument("name is required")); + } + let authz = authorize_workspace( + &state.store, + &state.admin_role, + &principal, + &req.workspace, + MinWorkspaceRole::User, + ) + .await?; + let workspace = super::workspace::resolve_workspace(state.store.as_ref(), &authz.workspace) + .await? + .name; + let sandbox = state.compute.start_sandbox(&workspace, &req.name).await?; + info!(sandbox_name = %req.name, "StartSandbox request completed successfully"); + Ok(Response::new(SandboxResponse { + sandbox: Some(sandbox), + })) +} + async fn sandbox_by_name( state: &Arc, workspace: &str, @@ -2725,7 +2814,7 @@ mod tests { .await }); tokio::time::timeout(std::time::Duration::from_secs(5), async { - while state.compute.delete_gate_entry_count() == 0 { + while state.compute.lifecycle_gate_entry_count() == 0 { tokio::time::sleep(std::time::Duration::from_millis(10)).await; } }) @@ -4426,6 +4515,31 @@ mod tests { Code::PermissionDenied, "handle_delete_sandbox should reject non-members with PermissionDenied" ); + + for result in [ + handle_stop_sandbox( + &state, + non_member_request(StopSandboxRequest { + workspace: "no-such-ws".into(), + name: "any".into(), + }), + ) + .await, + handle_start_sandbox( + &state, + non_member_request(StartSandboxRequest { + workspace: "no-such-ws".into(), + name: "any".into(), + }), + ) + .await, + ] { + assert_eq!( + result.unwrap_err().code(), + Code::PermissionDenied, + "lifecycle handlers should reject non-members" + ); + } } /// ID-based data-plane handlers must return `NOT_FOUND` — never diff --git a/crates/openshell-server/src/lib.rs b/crates/openshell-server/src/lib.rs index a7bf847d9a..1620d7e677 100644 --- a/crates/openshell-server/src/lib.rs +++ b/crates/openshell-server/src/lib.rs @@ -487,9 +487,9 @@ pub(crate) async fn run_server( let (shutdown_tx, shutdown_rx) = watch::channel(false); - // Resume sandboxes that were stopped during the previous gateway + // Start sandboxes that were stopped during the previous gateway // shutdown so the running compute state matches the persisted store. - // Runs before watchers spawn so the watch loop sees the post-resume + // Runs before watchers spawn so the watch loop sees the post-start // snapshot on its first poll. ensure_default_workspace(&store).await?; @@ -499,8 +499,8 @@ pub(crate) async fn run_server( ) .await?; - if let Err(err) = state.compute.resume_persisted_sandboxes().await { - warn!(error = %err, "Failed to resume persisted sandboxes during startup"); + if let Err(err) = state.compute.start_persisted_sandboxes().await { + warn!(error = %err, "Failed to start persisted sandboxes during startup"); } state.compute.spawn_watchers(shutdown_rx.clone()); @@ -1607,10 +1607,10 @@ mod tests { } #[tokio::test] - async fn failed_gateway_listener_bind_does_not_attempt_persisted_sandbox_resume() { + async fn failed_gateway_listener_bind_does_not_attempt_persisted_sandbox_start() { let occupied_listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let occupied_address = occupied_listener.local_addr().unwrap(); - let resume_attempted = AtomicBool::new(false); + let start_attempted = AtomicBool::new(false); let primary_address: SocketAddr = "127.0.0.1:0".parse().unwrap(); let result: openshell_core::Result<()> = async { @@ -1619,7 +1619,7 @@ mod tests { &[docker_listener_requirement(occupied_address)], ) .await?; - resume_attempted.store(true, Ordering::SeqCst); + start_attempted.store(true, Ordering::SeqCst); Ok(()) } .await; @@ -1629,8 +1629,8 @@ mod tests { "binding the occupied extra gateway address should fail" ); assert!( - !resume_attempted.load(Ordering::SeqCst), - "persisted sandbox resume must not run before every gateway listener is bound" + !start_attempted.load(Ordering::SeqCst), + "persisted sandbox start must not run before every gateway listener is bound" ); } diff --git a/crates/openshell-server/src/supervisor_session.rs b/crates/openshell-server/src/supervisor_session.rs index e6b8085151..11ef55978b 100644 --- a/crates/openshell-server/src/supervisor_session.rs +++ b/crates/openshell-server/src/supervisor_session.rs @@ -141,6 +141,20 @@ impl SupervisorSessionRegistry { self.sessions.lock().unwrap().remove(sandbox_id); } + /// Disconnect the current supervisor session for a sandbox. + /// + /// Lifecycle stop uses this to ensure a later start must establish + /// a fresh session before the sandbox can return to Ready. + pub fn disconnect(&self, sandbox_id: &str) -> bool { + let session = self.sessions.lock().unwrap().remove(sandbox_id); + if let Some(session) = session { + let _ = session.shutdown.send(()); + true + } else { + false + } + } + /// Remove the session only if its `session_id` matches the one we are /// cleaning up. Returns `true` if the entry was removed. /// diff --git a/crates/openshell-server/src/test_support.rs b/crates/openshell-server/src/test_support.rs index d1aa10da11..016e8d56af 100644 --- a/crates/openshell-server/src/test_support.rs +++ b/crates/openshell-server/src/test_support.rs @@ -11,9 +11,10 @@ use openshell_core::proto::compute::v1::{ DriverSandbox, GatewayListenerRequirement, GetCapabilitiesRequest, GetCapabilitiesResponse, GetGatewayListenerRequirementsRequest, GetGatewayListenerRequirementsResponse, GetSandboxRequest, GetSandboxResponse, ListSandboxesRequest, ListSandboxesResponse, - StopSandboxRequest, StopSandboxResponse, ValidateSandboxCreateRequest, - ValidateSandboxCreateResponse, WatchSandboxesEvent, WatchSandboxesRequest, - compute_driver_server::ComputeDriver, gateway_listener_requirement::Selector, + StartSandboxRequest, StartSandboxResponse, StopSandboxRequest, StopSandboxResponse, + ValidateSandboxCreateRequest, ValidateSandboxCreateResponse, WatchSandboxesEvent, + WatchSandboxesRequest, compute_driver_server::ComputeDriver, + gateway_listener_requirement::Selector, }; use std::collections::HashMap; #[cfg(unix)] @@ -51,6 +52,10 @@ pub enum FakeComputeDriverCall { sandbox_id: String, sandbox_name: String, }, + StartSandbox { + sandbox_id: String, + sandbox_name: String, + }, DeleteSandbox { sandbox_id: String, sandbox_name: String, @@ -344,6 +349,21 @@ impl ComputeDriver for FakeComputeDriver { Ok(Response::new(StopSandboxResponse {})) } + async fn start_sandbox( + &self, + request: Request, + ) -> Result, Status> { + self.record_traceparent(request.metadata()); + let request = request.into_inner(); + self.with_state(|state| { + state.calls.push(FakeComputeDriverCall::StartSandbox { + sandbox_id: request.sandbox_id, + sandbox_name: request.sandbox_name, + }); + }); + Ok(Response::new(StartSandboxResponse {})) + } + async fn delete_sandbox( &self, request: Request, diff --git a/crates/openshell-server/tests/common/mod.rs b/crates/openshell-server/tests/common/mod.rs index cfd7faa3d6..a2df4755f0 100644 --- a/crates/openshell-server/tests/common/mod.rs +++ b/crates/openshell-server/tests/common/mod.rs @@ -83,6 +83,20 @@ impl OpenShell for TestOpenShell { Ok(Response::new(SandboxResponse::default())) } + async fn stop_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + + async fn start_sandbox( + &self, + _request: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + async fn get_sandbox( &self, _request: tonic::Request, diff --git a/crates/openshell-server/tests/supervisor_relay_integration.rs b/crates/openshell-server/tests/supervisor_relay_integration.rs index be1af8f48c..86c7354647 100644 --- a/crates/openshell-server/tests/supervisor_relay_integration.rs +++ b/crates/openshell-server/tests/supervisor_relay_integration.rs @@ -130,6 +130,18 @@ impl OpenShell for RelayGateway { ) -> Result, Status> { Err(Status::unimplemented("unused")) } + async fn stop_sandbox( + &self, + _: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } + async fn start_sandbox( + &self, + _: tonic::Request, + ) -> Result, Status> { + Err(Status::unimplemented("unused")) + } async fn get_sandbox( &self, _: tonic::Request, diff --git a/crates/openshell-tui/src/lib.rs b/crates/openshell-tui/src/lib.rs index 40adc8d068..1f610015b4 100644 --- a/crates/openshell-tui/src/lib.rs +++ b/crates/openshell-tui/src/lib.rs @@ -2687,6 +2687,9 @@ fn phase_label(phase: i32) -> String { x if x == SandboxPhase::Ready as i32 => "Ready", x if x == SandboxPhase::Error as i32 => "Error", x if x == SandboxPhase::Deleting as i32 => "Deleting", + x if x == SandboxPhase::Stopping as i32 => "Stopping", + x if x == SandboxPhase::Stopped as i32 => "Stopped", + x if x == SandboxPhase::Starting as i32 => "Starting", _ => "Unknown", } .to_string() @@ -2716,6 +2719,18 @@ fn format_age(epoch_ms: i64) -> String { } } +#[cfg(test)] +mod phase_label_tests { + use super::*; + + #[test] + fn phase_label_covers_stop_and_start_lifecycle() { + assert_eq!(phase_label(SandboxPhase::Stopping as i32), "Stopping"); + assert_eq!(phase_label(SandboxPhase::Stopped as i32), "Stopped"); + assert_eq!(phase_label(SandboxPhase::Starting as i32), "Starting"); + } +} + /// Format epoch milliseconds as a human-readable UTC timestamp: `YYYY-MM-DD HH:MM`. fn format_timestamp(epoch_ms: i64) -> String { if epoch_ms <= 0 { diff --git a/crates/openshell-tui/src/ui/sandbox_detail.rs b/crates/openshell-tui/src/ui/sandbox_detail.rs index f212f21b06..434f369d39 100644 --- a/crates/openshell-tui/src/ui/sandbox_detail.rs +++ b/crates/openshell-tui/src/ui/sandbox_detail.rs @@ -23,15 +23,15 @@ pub fn draw(frame: &mut Frame<'_>, app: &App, area: Rect) { let phase_style = match phase { "Ready" => t.status_ok, - "Provisioning" => t.status_warn, + "Provisioning" | "Stopping" | "Starting" => t.status_warn, "Error" => t.status_err, _ => t.muted, }; let status_indicator = match phase { "Ready" => "●", - "Provisioning" => "◐", - "Error" => "○", + "Provisioning" | "Stopping" | "Starting" => "◐", + "Error" | "Stopped" => "○", _ => "…", }; diff --git a/crates/openshell-tui/src/ui/sandboxes.rs b/crates/openshell-tui/src/ui/sandboxes.rs index 4d239241b9..d927537189 100644 --- a/crates/openshell-tui/src/ui/sandboxes.rs +++ b/crates/openshell-tui/src/ui/sandboxes.rs @@ -41,7 +41,7 @@ pub fn draw(frame: &mut Frame<'_>, app: &App, area: Rect, focused: bool) { let phase_style = match phase { "Ready" => t.status_ok, - "Provisioning" => t.status_warn, + "Provisioning" | "Stopping" | "Starting" => t.status_warn, "Error" => t.status_err, _ => t.muted, }; diff --git a/docs/reference/sandbox-compute-drivers.mdx b/docs/reference/sandbox-compute-drivers.mdx index ea4c1a37b0..b05bf05581 100644 --- a/docs/reference/sandbox-compute-drivers.mdx +++ b/docs/reference/sandbox-compute-drivers.mdx @@ -8,10 +8,16 @@ keywords: "Generative AI, Cybersecurity, AI Agents, Sandboxing, Docker, Podman, position: 4 --- -The gateway's configured compute driver determines how OpenShell creates each sandbox. The CLI workflow stays the same across drivers: you create, connect to, inspect, and delete sandboxes through the gateway API. +The gateway's configured compute driver determines how OpenShell creates each sandbox. The CLI workflow stays the same across drivers: you create, connect to, inspect, stop, start, and delete sandboxes through the gateway API. Every compute driver runs the OpenShell supervisor inside the sandbox workload. The supervisor launches the agent process, applies policy, routes egress through the proxy, injects configured credentials, and maintains the gateway session. +Stop stops compute but retains the sandbox record and the driver's +persistent workspace boundary. Start reactivates the same driver resource. +Delete remains independent and removes compute plus driver-owned persistent +state. While a sandbox is stopped, gateway access paths and exposed services +remain unavailable. + ## Configure a Compute Driver Configure the compute driver on the gateway. Current releases accept one driver per gateway. Set `compute_drivers` in the gateway TOML file: @@ -129,6 +135,11 @@ For maintainer-level implementation details, refer to the [Docker driver README] Select Docker with `compute_drivers = ["docker"]` in `[openshell.gateway]`. Configure Docker driver values such as `socket_path`, `grpc_endpoint`, `network_name`, `supervisor_bin`, `supervisor_image`, `image_pull_policy`, `ssh_socket_path`, `sandbox_pids_limit`, and `guest_tls_*` in `[openshell.drivers.docker]`. When `socket_path` is unset, the driver uses the same responsive local socket selected by auto-detection. An explicitly selected Docker driver falls back to `/var/run/docker.sock` when no candidate responds. +Stop stops the existing Docker container without removing its writable +layer or attached volumes. Start starts that same container. A durably +stopped container stays stopped across gateway restart, and delete remains +responsible for removing it. + For GPU-backed Docker sandboxes, configure Docker CDI before starting the gateway so OpenShell can detect the daemon capability. ### Docker Driver Config Mounts @@ -197,6 +208,10 @@ Select Podman with `compute_drivers = ["podman"]` in `[openshell.gateway]`. Conf Podman sandboxes default to a 45-second graceful stop window before Podman escalates from `SIGTERM` to `SIGKILL`. Set `stop_timeout_secs` in gateway config, or `OPENSHELL_STOP_TIMEOUT` for the standalone driver, when a local runtime needs a different teardown window. +Stop stops the existing Podman container while retaining its named workspace +volume and driver-owned secrets. Start starts the same container. Delete is +the operation that removes the container and named volume. + For proxy-required networks, the Podman driver also accepts the corporate egress proxy keys `https_proxy`, `no_proxy`, `proxy_auth_file`, `proxy_auth_allow_insecure`, and `proxy_connect_by_hostname`. The supervisor chains policy-approved TLS tunnels through the proxy with HTTP CONNECT instead of dialing destinations directly. See the [Gateway Configuration File](./gateway-config) reference for the full contract, including the cleartext-credential acknowledgement and the validated-IP CONNECT behavior. On macOS with `podman machine`, the driver uses gvproxy's host-loopback IP, `192.168.127.254`, for sandbox host aliases by default. Set `host_gateway_ip` only when your Podman machine uses a non-standard host-loopback address. On Linux, an empty `host_gateway_ip` keeps Podman's `host-gateway` resolver behavior. Direct local callbacks from rootless Podman require Podman to report the pasta network helper. Slirp4netns, other helpers, and Podman versions that do not report their helper require an explicitly remote `grpc_endpoint`; otherwise the gateway fails startup rather than leaving sandbox callbacks unreachable. Rootful Podman continues to use the configured network's bridge gateway address. @@ -269,6 +284,10 @@ VM sandbox creation follows the same progress model as Kubernetes-backed sandbox On gateway restart, the gateway starts a fresh VM driver process. The driver scans its state directory for accepted sandbox launch records, restarts those VMs, and reuses each sandbox's existing `overlay.ext4` so files written inside the sandbox remain available after the supervisor reconnects. +Stopped VM state directories contain a marker that prevents startup from +launching the VM. The driver retains `sandbox.pb`, `overlay.ext4`, and extension +state, then removes the marker and restores the same overlay on start. + For maintainer-level implementation details, refer to the [VM driver README](https://github.com/NVIDIA/OpenShell/blob/main/crates/openshell-driver-vm/README.md). ### Enable the VM Driver @@ -358,6 +377,11 @@ process/binary identity through `/proc/`. The Kubernetes driver creates namespaced `agents.x-k8s.io` `Sandbox` resources from the Kubernetes SIG Apps [agent-sandbox](https://github.com/kubernetes-sigs/agent-sandbox) project. It detects the served Sandbox API at runtime, caches the selected API version for the gateway process, and uses `v1beta1` when available before falling back to `v1alpha1`, so supported Agent Sandbox installations work without version-specific operator configuration. The Agent Sandbox controller turns those resources into sandbox pods and related storage. +Stop patches the existing resource rather than deleting it. For `v1beta1`, +the driver sets `spec.operatingMode` to `Suspended` or `Running`. For +`v1alpha1`, it sets `spec.replicas` to `0` or `1`. The Sandbox resource and its +workspace PVC keep their identity across both operations. + If Agent Sandbox is upgraded in place, restart the OpenShell gateway after the controller and CRD rollout completes so the gateway can detect the served API versions again. diff --git a/docs/sandboxes/manage-sandboxes.mdx b/docs/sandboxes/manage-sandboxes.mdx index f6596640a8..abd95d130a 100644 --- a/docs/sandboxes/manage-sandboxes.mdx +++ b/docs/sandboxes/manage-sandboxes.mdx @@ -438,6 +438,26 @@ back to an unfiltered upload and prints a warning. Pass `--no-git-ignore` to opt into unfiltered uploads explicitly, upload a path outside the Git work tree, or force-add the intended files if they should remain Git-aware. +## Stop and Start Sandboxes + +Stop compute when you want to retain a sandbox and its persistent workspace +without keeping its container, pod, or VM running: + +```shell +openshell sandbox stop my-sandbox +openshell sandbox start my-sandbox +``` + +The name is optional and defaults to the last-used sandbox. Stop stops local +background forwards and waits for the `Stopped` phase. Start waits until the +same sandbox returns to `Ready`. While stopped, you cannot connect, execute +commands, transfer files, forward ports, or reach exposed services. Policies, +provider attachments, settings, service definitions, and persistent workspace +data remain associated with the sandbox. + +Stop and start are idempotent. Delete a stopped sandbox normally when you +no longer need its retained state. + ## Delete Sandboxes Deleting a sandbox stops all processes, releases resources, and purges injected credentials. @@ -454,6 +474,9 @@ Every sandbox moves through a defined set of phases: | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Provisioning | The runtime is setting up the sandbox environment, or the gateway is waiting for the sandbox supervisor to establish its authenticated control session. | | Ready | The sandbox is running and its supervisor control session is connected. You can connect, execute commands, sync files, and view logs. | +| Stopping | The gateway accepted a stop request and is stopping compute while retaining persistent state. | +| Stopped | Compute is stopped and access is unavailable. The sandbox record and driver-owned persistent workspace remain. | +| Starting | Compute is starting. The sandbox becomes usable only after a fresh supervisor session connects. | | Error | Something went wrong during provisioning or execution. Check logs with `openshell logs` for details. | | Deleting | The sandbox is being torn down. The system releases resources and purges credentials. | diff --git a/e2e/rust/Cargo.toml b/e2e/rust/Cargo.toml index 3353f07af7..76881d1f5e 100644 --- a/e2e/rust/Cargo.toml +++ b/e2e/rust/Cargo.toml @@ -55,8 +55,8 @@ path = "tests/driver_config_volume.rs" required-features = ["e2e-local-container-driver"] [[test]] -name = "gateway_resume" -path = "tests/gateway_resume.rs" +name = "gateway_start" +path = "tests/gateway_start.rs" required-features = ["e2e-docker"] [[test]] @@ -65,8 +65,8 @@ path = "tests/local_driver_token_restart.rs" required-features = ["e2e"] [[test]] -name = "podman_gateway_resume" -path = "tests/podman_gateway_resume.rs" +name = "podman_gateway_start" +path = "tests/podman_gateway_start.rs" required-features = ["e2e-podman"] [[test]] @@ -80,8 +80,8 @@ path = "tests/podman_oci_identity.rs" required-features = ["e2e-podman"] [[test]] -name = "vm_gateway_resume" -path = "tests/vm_gateway_resume.rs" +name = "vm_gateway_start" +path = "tests/vm_gateway_start.rs" required-features = ["e2e-vm"] [[test]] diff --git a/e2e/rust/e2e-vm.sh b/e2e/rust/e2e-vm.sh index 43b573f867..26671ccb86 100755 --- a/e2e/rust/e2e-vm.sh +++ b/e2e/rust/e2e-vm.sh @@ -366,5 +366,5 @@ if [ -n "${E2E_TEST_OVERRIDE}" ]; then else run_e2e_test smoke run_e2e_test host_gateway_alias - run_e2e_test vm_gateway_resume + run_e2e_test vm_gateway_start fi diff --git a/e2e/rust/tests/gateway_resume.rs b/e2e/rust/tests/gateway_start.rs similarity index 90% rename from e2e/rust/tests/gateway_resume.rs rename to e2e/rust/tests/gateway_start.rs index 8f850e485d..3984cf1907 100644 --- a/e2e/rust/tests/gateway_resume.rs +++ b/e2e/rust/tests/gateway_start.rs @@ -3,7 +3,7 @@ #![cfg(feature = "e2e")] -//! E2E coverage for resuming Docker sandboxes after a standalone gateway restart. +//! E2E coverage for starting Docker sandboxes after a standalone gateway restart. //! //! This intentionally targets the Docker-driver gateway started by //! `e2e/with-docker-gateway.sh`. Existing-endpoint E2E runs do not own the @@ -20,8 +20,8 @@ use openshell_e2e::harness::sandbox::SandboxGuard; use tokio::time::sleep; const MANAGED_BY_LABEL_FILTER: &str = "label=openshell.ai/managed-by=openshell"; -const READY_MARKER: &str = "gateway-resume-ready"; -const RESUME_FILE: &str = "/sandbox/gateway-resume-state"; +const READY_MARKER: &str = "gateway-start-ready"; +const START_FILE: &str = "/sandbox/gateway-start-state"; const SANDBOX_NAMESPACE_LABEL: &str = "openshell.ai/sandbox-namespace"; const SANDBOX_NAME_LABEL: &str = "openshell.ai/sandbox-name"; @@ -117,17 +117,17 @@ async fn wait_for_container_running( } #[tokio::test] -async fn docker_gateway_restart_resumes_running_sandbox() { +async fn docker_gateway_restart_starts_running_sandbox() { let Some(gateway) = ManagedGateway::from_env().expect("load managed e2e gateway metadata") else { - eprintln!("Skipping gateway resume test: e2e gateway is not managed by this test run"); + eprintln!("Skipping gateway start test: e2e gateway is not managed by this test run"); return; }; let Some(namespace) = std::env::var("OPENSHELL_E2E_DOCKER_NETWORK_NAME") .ok() .filter(|value| !value.trim().is_empty()) else { - eprintln!("Skipping gateway resume test: Docker e2e namespace is unavailable"); + eprintln!("Skipping gateway start test: Docker e2e namespace is unavailable"); return; }; @@ -136,14 +136,14 @@ async fn docker_gateway_restart_resumes_running_sandbox() { .expect("gateway should start healthy"); let script = format!( - "echo before-restart > {RESUME_FILE}; echo {READY_MARKER}; while true; do sleep 1; done" + "echo before-restart > {START_FILE}; echo {READY_MARKER}; while true; do sleep 1; done" ); let mut sandbox = SandboxGuard::create_keep(&["sh", "-lc", &script], READY_MARKER) .await .expect("create long-running sandbox"); let before_restart = sandbox - .exec(&["cat", RESUME_FILE]) + .exec(&["cat", START_FILE]) .await .expect("read sandbox state before restart"); assert!( @@ -166,7 +166,7 @@ async fn docker_gateway_restart_resumes_running_sandbox() { .expect("gateway should become healthy after restart"); wait_for_container_running(&namespace, &sandbox.name, true, Duration::from_secs(120)) .await - .expect("gateway startup should resume the Docker sandbox container"); + .expect("gateway startup should start the Docker sandbox container"); let names = sandbox_names().await.expect("list sandboxes after restart"); assert!( @@ -177,7 +177,7 @@ async fn docker_gateway_restart_resumes_running_sandbox() { wait_for_sandbox_exec_contains( &sandbox.name, - &["cat", RESUME_FILE], + &["cat", START_FILE], "before-restart", Duration::from_secs(240), ) diff --git a/e2e/rust/tests/podman_gateway_resume.rs b/e2e/rust/tests/podman_gateway_start.rs similarity index 77% rename from e2e/rust/tests/podman_gateway_resume.rs rename to e2e/rust/tests/podman_gateway_start.rs index 2600202537..e72769cb80 100644 --- a/e2e/rust/tests/podman_gateway_resume.rs +++ b/e2e/rust/tests/podman_gateway_start.rs @@ -3,12 +3,12 @@ #![cfg(feature = "e2e-podman")] -//! Podman-specific E2E coverage for resuming sandboxes after a standalone +//! Podman-specific E2E coverage for starting sandboxes after a standalone //! gateway restart. //! //! Unlike the Docker driver, Podman does not stop sandbox containers when the //! gateway process exits — the containers keep running and the restarted -//! gateway re-adopts them. This test follows the `vm_gateway_resume.rs` +//! gateway re-adopts them. This test follows the `vm_gateway_start.rs` //! pattern: verify sandbox survival at the application level without asserting //! intermediate container-state transitions. @@ -20,19 +20,19 @@ use openshell_e2e::harness::cli::{ use openshell_e2e::harness::gateway::ManagedGateway; use openshell_e2e::harness::sandbox::SandboxGuard; -const READY_MARKER: &str = "podman-gateway-resume-ready"; -const RESUME_FILE: &str = "/sandbox/podman-gateway-resume-state"; +const READY_MARKER: &str = "podman-gateway-start-ready"; +const START_FILE: &str = "/sandbox/podman-gateway-start-state"; #[tokio::test] -async fn podman_gateway_restart_resumes_running_sandbox() { +async fn podman_gateway_restart_starts_running_sandbox() { if std::env::var("OPENSHELL_E2E_DRIVER").as_deref() != Ok("podman") { - eprintln!("Skipping Podman gateway resume test: e2e driver is not podman"); + eprintln!("Skipping Podman gateway start test: e2e driver is not podman"); return; } let Some(gateway) = ManagedGateway::from_env().expect("load managed e2e gateway metadata") else { eprintln!( - "Skipping Podman gateway resume test: e2e gateway is not managed by this test run" + "Skipping Podman gateway start test: e2e gateway is not managed by this test run" ); return; }; @@ -42,14 +42,14 @@ async fn podman_gateway_restart_resumes_running_sandbox() { .expect("gateway should start healthy"); let script = format!( - "echo before-restart > {RESUME_FILE}; echo {READY_MARKER}; while true; do sleep 1; done" + "echo before-restart > {START_FILE}; echo {READY_MARKER}; while true; do sleep 1; done" ); let mut sandbox = SandboxGuard::create_keep(&["sh", "-lc", &script], READY_MARKER) .await .expect("create long-running Podman sandbox"); let before_restart = sandbox - .exec(&["cat", RESUME_FILE]) + .exec(&["cat", START_FILE]) .await .expect("read Podman sandbox state before restart"); assert!( @@ -72,7 +72,7 @@ async fn podman_gateway_restart_resumes_running_sandbox() { wait_for_sandbox_exec_contains( &sandbox.name, - &["cat", RESUME_FILE], + &["cat", START_FILE], "before-restart", Duration::from_secs(240), ) diff --git a/e2e/rust/tests/sandbox_lifecycle.rs b/e2e/rust/tests/sandbox_lifecycle.rs index 4b832411fe..75e34b3b4f 100644 --- a/e2e/rust/tests/sandbox_lifecycle.rs +++ b/e2e/rust/tests/sandbox_lifecycle.rs @@ -8,6 +8,7 @@ use std::time::Duration; use openshell_e2e::harness::binary::{openshell_cmd, openshell_tty_cmd}; use openshell_e2e::harness::output::{extract_field, strip_ansi}; +use openshell_e2e::harness::sandbox::SandboxGuard; use tokio::time::{Instant, sleep}; const SANDBOX_PRESENCE_TIMEOUT: Duration = Duration::from_secs(30); @@ -105,6 +106,118 @@ async fn delete_sandbox(name: &str) { let _ = cmd.status().await; } +async fn run_sandbox_lifecycle_command(operation: &str, name: &str) -> String { + let mut cmd = openshell_cmd(); + cmd.args(["sandbox", operation, name]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let output = cmd + .output() + .await + .unwrap_or_else(|error| panic!("spawn openshell sandbox {operation}: {error}")); + let combined = normalize_output(&format!( + "{}{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + )); + assert!( + output.status.success(), + "sandbox {operation} should succeed (exit {:?}):\n{combined}", + output.status.code(), + ); + combined +} + +#[tokio::test] +async fn sandbox_stop_start_preserves_workspace() { + const SENTINEL: &str = "openshell-stop-start-sentinel"; + const SENTINEL_PATH: &str = "/sandbox/.openshell-stop-start-e2e"; + let write_sentinel = format!("printf '%s\\n' '{SENTINEL}' > '{SENTINEL_PATH}'"); + + let mut sandbox = SandboxGuard::create(&["--", "sh", "-lc", &write_sentinel]) + .await + .expect("sandbox create should write the workspace sentinel"); + + let stop_output = run_sandbox_lifecycle_command("stop", &sandbox.name).await; + assert!( + stop_output.contains("Stopped sandbox"), + "expected stop confirmation in:\n{stop_output}", + ); + + let mut exec_cmd = openshell_cmd(); + exec_cmd + .args([ + "sandbox", + "exec", + "--name", + &sandbox.name, + "--no-tty", + "--", + "cat", + SENTINEL_PATH, + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + let stopped_exec = exec_cmd + .output() + .await + .expect("spawn openshell sandbox exec while stopped"); + assert!( + !stopped_exec.status.success(), + "sandbox exec should fail while stopped" + ); + + let start_output = run_sandbox_lifecycle_command("start", &sandbox.name).await; + assert!( + start_output.contains("Started sandbox"), + "expected start confirmation in:\n{start_output}", + ); + + let sentinel = sandbox + .exec(&["cat", SENTINEL_PATH]) + .await + .expect("sandbox exec should succeed after start"); + assert!( + sentinel.lines().any(|line| line.trim() == SENTINEL), + "workspace sentinel should survive stop and start:\n{sentinel}", + ); + + sandbox.cleanup().await; +} + +#[tokio::test] +async fn sandbox_can_be_deleted_while_stopped() { + let mut sandbox = SandboxGuard::create(&["--", "true"]) + .await + .expect("sandbox create should succeed"); + + let stop_output = run_sandbox_lifecycle_command("stop", &sandbox.name).await; + assert!( + stop_output.contains("Stopped sandbox"), + "expected stop confirmation in:\n{stop_output}", + ); + + let delete_output = run_sandbox_lifecycle_command("delete", &sandbox.name).await; + assert!( + delete_output.contains("Deleted sandbox"), + "expected delete confirmation in:\n{delete_output}", + ); + + if let Err(last_sandbox_list) = assert_sandbox_presence_eventually(&sandbox.name, false).await { + sandbox.cleanup().await; + panic!( + "stopped sandbox {} should be deleted without starting after \ + {SANDBOX_PRESENCE_TIMEOUT:?}; last observed sandbox list: {last_sandbox_list:?}", + sandbox.name, + ); + } + + // Mark the guard cleaned up. Its idempotent delete is harmless now that + // the lifecycle operation above has removed the sandbox. + sandbox.cleanup().await; +} + #[tokio::test] async fn sandbox_create_keeps_sandbox_after_tty_command_by_default() { let mut cmd = openshell_tty_cmd(&["sandbox", "create", "--", "echo", "OK"]); diff --git a/e2e/rust/tests/vm_gateway_resume.rs b/e2e/rust/tests/vm_gateway_start.rs similarity index 79% rename from e2e/rust/tests/vm_gateway_resume.rs rename to e2e/rust/tests/vm_gateway_start.rs index 4c502bb9c9..923198668b 100644 --- a/e2e/rust/tests/vm_gateway_resume.rs +++ b/e2e/rust/tests/vm_gateway_start.rs @@ -3,7 +3,7 @@ #![cfg(feature = "e2e-vm")] -//! VM-specific E2E coverage for resuming sandboxes after a standalone gateway +//! VM-specific E2E coverage for starting sandboxes after a standalone gateway //! restart. //! //! This test is gated behind the `e2e-vm` feature because it requires the VM @@ -17,18 +17,18 @@ use openshell_e2e::harness::cli::{ use openshell_e2e::harness::gateway::ManagedGateway; use openshell_e2e::harness::sandbox::SandboxGuard; -const READY_MARKER: &str = "vm-gateway-resume-ready"; -const RESUME_FILE: &str = "/sandbox/vm-gateway-resume-state"; +const READY_MARKER: &str = "vm-gateway-start-ready"; +const START_FILE: &str = "/sandbox/vm-gateway-start-state"; #[tokio::test] -async fn vm_gateway_restart_resumes_running_sandbox() { +async fn vm_gateway_restart_starts_running_sandbox() { if std::env::var("OPENSHELL_E2E_DRIVER").as_deref() != Ok("vm") { - eprintln!("Skipping VM gateway resume test: e2e driver is not vm"); + eprintln!("Skipping VM gateway start test: e2e driver is not vm"); return; } let Some(gateway) = ManagedGateway::from_env().expect("load managed e2e gateway metadata") else { - eprintln!("Skipping VM gateway resume test: e2e gateway is not managed by this test run"); + eprintln!("Skipping VM gateway start test: e2e gateway is not managed by this test run"); return; }; @@ -40,14 +40,14 @@ async fn vm_gateway_restart_resumes_running_sandbox() { // overlay. Flush the marker before reporting readiness so the assertion // verifies durable overlay state rather than guest page-cache timing. let script = format!( - "echo before-restart > {RESUME_FILE}; sync; echo {READY_MARKER}; while true; do sleep 1; done" + "echo before-restart > {START_FILE}; sync; echo {READY_MARKER}; while true; do sleep 1; done" ); let mut sandbox = SandboxGuard::create_keep(&["sh", "-lc", &script], READY_MARKER) .await .expect("create long-running VM sandbox"); let before_restart = sandbox - .exec(&["cat", RESUME_FILE]) + .exec(&["cat", START_FILE]) .await .expect("read VM sandbox state before restart"); assert!( @@ -70,7 +70,7 @@ async fn vm_gateway_restart_resumes_running_sandbox() { wait_for_sandbox_exec_contains( &sandbox.name, - &["cat", RESUME_FILE], + &["cat", START_FILE], "before-restart", Duration::from_secs(240), ) diff --git a/proto/compute_driver.proto b/proto/compute_driver.proto index e3f18af19f..3a0b7609ab 100644 --- a/proto/compute_driver.proto +++ b/proto/compute_driver.proto @@ -40,9 +40,12 @@ service ComputeDriver { // Provision platform resources for a sandbox. rpc CreateSandbox(CreateSandboxRequest) returns (CreateSandboxResponse); - // Stop platform resources for a sandbox without deleting its record. + // Idempotently stop platform resources without deleting persistent state. rpc StopSandbox(StopSandboxRequest) returns (StopSandboxResponse); + // Idempotently start platform resources for a stopped sandbox. + rpc StartSandbox(StartSandboxRequest) returns (StartSandboxResponse); + // Tear down platform resources for a sandbox. rpc DeleteSandbox(DeleteSandboxRequest) returns (DeleteSandboxResponse); @@ -286,6 +289,15 @@ message StopSandboxRequest { message StopSandboxResponse {} +message StartSandboxRequest { + // Stable sandbox ID stored by the gateway. + string sandbox_id = 1; + // Compute-runtime name used by the driver. + string sandbox_name = 2; +} + +message StartSandboxResponse {} + message DeleteSandboxRequest { // Stable sandbox ID stored by the gateway. string sandbox_id = 1; diff --git a/proto/openshell.proto b/proto/openshell.proto index 49f6581e7c..5f3b660ae4 100644 --- a/proto/openshell.proto +++ b/proto/openshell.proto @@ -108,6 +108,24 @@ service OpenShell { }; } + // Stop a sandbox while retaining its persistent state. + rpc StopSandbox(StopSandboxRequest) returns (SandboxResponse) { + option (openshell.options.v1.authorization) = { + auth_mode: "bearer" + scope: "sandbox:write" + workspace_role: "user" + }; + } + + // Start a previously stopped sandbox. + rpc StartSandbox(StartSandboxRequest) returns (SandboxResponse) { + option (openshell.options.v1.authorization) = { + auth_mode: "bearer" + scope: "sandbox:write" + workspace_role: "user" + }; + } + // Create a short-lived SSH session for a sandbox. rpc CreateSshSession(CreateSshSessionRequest) returns (CreateSshSessionResponse) { option (openshell.options.v1.authorization) = { @@ -879,6 +897,9 @@ enum SandboxPhase { SANDBOX_PHASE_ERROR = 3; SANDBOX_PHASE_DELETING = 4; SANDBOX_PHASE_UNKNOWN = 5; + SANDBOX_PHASE_STOPPING = 6; + SANDBOX_PHASE_STOPPED = 7; + SANDBOX_PHASE_STARTING = 8; } // Public platform event exposed on the sandbox watch stream. @@ -976,6 +997,22 @@ message DeleteSandboxRequest { string workspace = 2; } +// Stop sandbox request. +message StopSandboxRequest { + // Sandbox name (canonical lookup key). + string name = 1; + // Workspace scope. Empty defaults to "default". + string workspace = 2; +} + +// Start sandbox request. +message StartSandboxRequest { + // Sandbox name (canonical lookup key). + string name = 1; + // Workspace scope. Empty defaults to "default". + string workspace = 2; +} + // Sandbox response. message SandboxResponse { Sandbox sandbox = 1; diff --git a/python/openshell/sandbox.py b/python/openshell/sandbox.py index 197f5e65b6..a76be8dd13 100644 --- a/python/openshell/sandbox.py +++ b/python/openshell/sandbox.py @@ -251,6 +251,14 @@ def exec_python( def delete(self) -> bool: return self._client.delete(self.sandbox.name, workspace=self._workspace) + def stop(self) -> SandboxRef: + self.sandbox = self._client.stop(self.sandbox.name, workspace=self._workspace) + return self.sandbox + + def start(self) -> SandboxRef: + self.sandbox = self._client.start(self.sandbox.name, workspace=self._workspace) + return self.sandbox + class SandboxClient: """gRPC client for sandbox CRUD and command execution.""" @@ -547,6 +555,20 @@ def delete(self, sandbox_name: str, *, workspace: str) -> bool: ) return bool(response.deleted) + def stop(self, sandbox_name: str, *, workspace: str) -> SandboxRef: + response = self._stub.StopSandbox( + openshell_pb2.StopSandboxRequest(name=sandbox_name, workspace=workspace), + timeout=self._timeout, + ) + return _sandbox_ref(response.sandbox) + + def start(self, sandbox_name: str, *, workspace: str) -> SandboxRef: + response = self._stub.StartSandbox( + openshell_pb2.StartSandboxRequest(name=sandbox_name, workspace=workspace), + timeout=self._timeout, + ) + return _sandbox_ref(response.sandbox) + def wait_deleted( self, sandbox_name: str, *, workspace: str, timeout_seconds: float = 60.0 ) -> None: @@ -566,16 +588,46 @@ def wait_deleted( def wait_ready( self, sandbox_name: str, *, workspace: str, timeout_seconds: float = 300.0 + ) -> SandboxRef: + return self._wait_for_phase( + sandbox_name, + workspace=workspace, + target_phase=openshell_pb2.SANDBOX_PHASE_READY, + target_name="ready", + timeout_seconds=timeout_seconds, + ) + + def wait_stopped( + self, sandbox_name: str, *, workspace: str, timeout_seconds: float = 300.0 + ) -> SandboxRef: + return self._wait_for_phase( + sandbox_name, + workspace=workspace, + target_phase=openshell_pb2.SANDBOX_PHASE_STOPPED, + target_name="stopped", + timeout_seconds=timeout_seconds, + ) + + def _wait_for_phase( + self, + sandbox_name: str, + *, + workspace: str, + target_phase: int, + target_name: str, + timeout_seconds: float, ) -> SandboxRef: deadline = time.time() + timeout_seconds while time.time() < deadline: sandbox = self.get(sandbox_name, workspace=workspace) - if sandbox.status.phase == openshell_pb2.SANDBOX_PHASE_READY: + if sandbox.status.phase == target_phase: return sandbox if sandbox.status.phase == openshell_pb2.SANDBOX_PHASE_ERROR: raise SandboxError(f"sandbox {sandbox_name} entered error phase") time.sleep(1) - raise SandboxError(f"sandbox {sandbox_name} was not ready within timeout") + raise SandboxError( + f"sandbox {sandbox_name} was not {target_name} within timeout" + ) def exec_stream( self, diff --git a/python/openshell/sandbox_test.py b/python/openshell/sandbox_test.py index 70e5428d1f..9ff84341e7 100644 --- a/python/openshell/sandbox_test.py +++ b/python/openshell/sandbox_test.py @@ -1543,6 +1543,8 @@ def __init__(self, listed: list[openshell_pb2.Sandbox] | None = None) -> None: self.list_request: openshell_pb2.ListSandboxesRequest | None = None self.get_request: openshell_pb2.GetSandboxRequest | None = None self.delete_request: openshell_pb2.DeleteSandboxRequest | None = None + self.stop_request: openshell_pb2.StopSandboxRequest | None = None + self.start_request: openshell_pb2.StartSandboxRequest | None = None self._listed = listed or [] def GetSandbox( @@ -1567,6 +1569,38 @@ def DeleteSandbox( _ = timeout return SimpleNamespace(deleted=True) + def StopSandbox( + self, + request: openshell_pb2.StopSandboxRequest, + timeout: float | None = None, + ) -> Any: + self.stop_request = request + _ = timeout + return SimpleNamespace( + sandbox=_make_sandbox_proto( + "sandbox-1", + request.name, + phase=openshell_pb2.SANDBOX_PHASE_STOPPED, + workspace=request.workspace, + ) + ) + + def StartSandbox( + self, + request: openshell_pb2.StartSandboxRequest, + timeout: float | None = None, + ) -> Any: + self.start_request = request + _ = timeout + return SimpleNamespace( + sandbox=_make_sandbox_proto( + "sandbox-1", + request.name, + phase=openshell_pb2.SANDBOX_PHASE_STARTING, + workspace=request.workspace, + ) + ) + def CreateSandbox( self, request: openshell_pb2.CreateSandboxRequest, @@ -1641,6 +1675,23 @@ def test_create_forwards_name_and_labels() -> None: assert dict(ref.labels) == {"aiq": "deep-research"} +def test_stop_and_start_forward_workspace_and_return_phase() -> None: + stub = _FakeSandboxStub() + client = _client_with_fake_stub(stub) + + stopped = client.stop("job-1", workspace="team-a") + assert stub.stop_request is not None + assert stub.stop_request.name == "job-1" + assert stub.stop_request.workspace == "team-a" + assert stopped.phase == openshell_pb2.SANDBOX_PHASE_STOPPED + + starting = client.start("job-1", workspace="team-a") + assert stub.start_request is not None + assert stub.start_request.name == "job-1" + assert stub.start_request.workspace == "team-a" + assert starting.phase == openshell_pb2.SANDBOX_PHASE_STARTING + + def test_create_without_args_sends_empty_metadata() -> None: stub = _FakeSandboxStub() client = _client_with_fake_stub(stub) diff --git a/rfc/0011-multi-player-design/README.md b/rfc/0011-multi-player-design/README.md index f28e140364..32a2f584e1 100644 --- a/rfc/0011-multi-player-design/README.md +++ b/rfc/0011-multi-player-design/README.md @@ -818,9 +818,9 @@ workspace authorization path. The gateway is the actor. workspace — the driver has no workspace concept. The gateway must query all stored sandboxes across all workspaces to produce the full set for comparison. -- **Startup resume** (`resume_persisted_sandboxes`). On gateway startup, the - resume path iterates all stored sandboxes whose phase indicates they should - be running and asks the driver to resume each one. This must cover all +- **Startup start** (`start_persisted_sandboxes`). On gateway startup, the + start path iterates all stored sandboxes whose phase indicates they should + be running and asks the driver to start each one. This must cover all workspaces. - **Provider credential refresh** (`refresh_provider_credential`). A background @@ -1144,7 +1144,7 @@ foundations. The work can be phased to deliver value incrementally: Backward compatibility is desirable but not a hard requirement at this stage — existing users must recreate service endpoints when upgrading. Add a cross-workspace `list_by_type(object_type, limit, offset)` store method - for internal infrastructure operations (reconciler, resume, provider refresh) + for internal infrastructure operations (reconciler, start, provider refresh) that need to query workspace-scoped resources across all workspaces. Thread workspace through `StoredProviderCredentialRefreshState` so the provider refresh worker can unambiguously resolve workspace-scoped providers — with @@ -1271,7 +1271,7 @@ depend only on Phase 1. has no access-control gate — it is a persistence-layer primitive. Authorization for cross-workspace queries is enforced at the gRPC handler level (Platform Admin check for `all_workspaces` on list RPCs) and by code-level access - control for internal operations (only the reconciler, resume, and refresh + control for internal operations (only the reconciler, start, and refresh worker call it). This relies on internal code discipline rather than an enforced store-level boundary. A future extension could add a store-level caller identity parameter if defense-in-depth is desired. diff --git a/sdk/go/openshell/v1/internal/converter/sandbox.go b/sdk/go/openshell/v1/internal/converter/sandbox.go index b522454b83..7dbd39968e 100644 --- a/sdk/go/openshell/v1/internal/converter/sandbox.go +++ b/sdk/go/openshell/v1/internal/converter/sandbox.go @@ -110,6 +110,12 @@ func SandboxPhaseFromProto(phase pb.SandboxPhase) types.SandboxPhase { 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 default: return types.SandboxUnknown } @@ -128,6 +134,12 @@ func SandboxPhaseToProto(phase types.SandboxPhase) pb.SandboxPhase { 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 default: return pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN } diff --git a/sdk/go/openshell/v1/internal/converter/sandbox_test.go b/sdk/go/openshell/v1/internal/converter/sandbox_test.go index 8cbc4d3b81..258f433408 100644 --- a/sdk/go/openshell/v1/internal/converter/sandbox_test.go +++ b/sdk/go/openshell/v1/internal/converter/sandbox_test.go @@ -142,6 +142,9 @@ func TestSandboxPhaseFromProto(t *testing.T) { {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_STARTING, v1.SandboxStarting}, {pb.SandboxPhase_SANDBOX_PHASE_UNSPECIFIED, v1.SandboxUnknown}, {pb.SandboxPhase(999), v1.SandboxUnknown}, } @@ -161,6 +164,9 @@ func TestSandboxPhaseToProto(t *testing.T) { {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.SandboxStarting, pb.SandboxPhase_SANDBOX_PHASE_STARTING}, {v1.SandboxPhase("bogus"), pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN}, } diff --git a/sdk/go/openshell/v1/sandbox.go b/sdk/go/openshell/v1/sandbox.go index 2dfc6ba8ac..6123ecd473 100644 --- a/sdk/go/openshell/v1/sandbox.go +++ b/sdk/go/openshell/v1/sandbox.go @@ -56,11 +56,14 @@ type SandboxInterface interface { Create(ctx context.Context, workspace, name string, spec *SandboxSpec, labels map[string]string) (*Sandbox, error) Get(ctx context.Context, workspace, name string) (*Sandbox, error) List(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) 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(ctx context.Context, workspace, sandboxName string) ([]*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 retrieves log entries for a sandbox. The sandbox is resolved // by name (an internal Get call translates name to ID). Use diff --git a/sdk/go/openshell/v1/sandbox_client.go b/sdk/go/openshell/v1/sandbox_client.go index 6c38db7811..8cf5d89a5d 100644 --- a/sdk/go/openshell/v1/sandbox_client.go +++ b/sdk/go/openshell/v1/sandbox_client.go @@ -91,6 +91,28 @@ func (s *sandboxClient) Delete(ctx context.Context, workspace, name string) erro return nil } +func (s *sandboxClient) Stop(ctx context.Context, workspace, name string) (*Sandbox, error) { + resp, err := s.client.StopSandbox(ctx, &pb.StopSandboxRequest{ + Name: name, + Workspace: 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, + Workspace: workspace, + }) + 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{ SandboxName: sandboxName, @@ -140,6 +162,14 @@ func (s *sandboxClient) ListProviders(ctx context.Context, workspace, sandboxNam } 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) 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 @@ -150,7 +180,7 @@ func (s *sandboxClient) WaitReady(ctx context.Context, workspace, name string, o return nil, err } - if sb.Status.Phase == SandboxReady { + if sb.Status.Phase == target { return sb, nil } if sb.Status.Phase == SandboxError { @@ -172,7 +202,7 @@ func (s *sandboxClient) WaitReady(ctx context.Context, workspace, name string, o if err != nil { return nil, err } - if sb.Status.Phase == SandboxReady { + if sb.Status.Phase == target { return sb, nil } if sb.Status.Phase == SandboxError { diff --git a/sdk/go/openshell/v1/sandbox_client_test.go b/sdk/go/openshell/v1/sandbox_client_test.go index 2574348ec4..bf06e852d1 100644 --- a/sdk/go/openshell/v1/sandbox_client_test.go +++ b/sdk/go/openshell/v1/sandbox_client_test.go @@ -125,6 +125,28 @@ func (s *mockSandboxServer) DeleteSandbox(_ context.Context, req *pb.DeleteSandb return &pb.DeleteSandboxResponse{Deleted: true}, nil } +func (s *mockSandboxServer) StopSandbox(_ context.Context, req *pb.StopSandboxRequest) (*pb.SandboxResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + sb, ok := s.sandboxes[req.GetName()] + if !ok { + return nil, status.Errorf(codes.NotFound, "sandbox %q not found", req.GetName()) + } + 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() + sb, ok := s.sandboxes[req.GetName()] + if !ok { + return nil, status.Errorf(codes.NotFound, "sandbox %q not found", req.GetName()) + } + 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() @@ -358,6 +380,24 @@ func TestSandboxDelete_NotFound(t *testing.T) { 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) +} + // --- T030: AttachProvider, DetachProvider, ListProviders tests --- func TestSandboxAttachProvider(t *testing.T) { @@ -472,6 +512,20 @@ func TestSandboxListProviders_Error(t *testing.T) { // --- 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{ diff --git a/sdk/go/openshell/v1/types.go b/sdk/go/openshell/v1/types.go index 012811cabb..59229ac0b1 100644 --- a/sdk/go/openshell/v1/types.go +++ b/sdk/go/openshell/v1/types.go @@ -17,6 +17,9 @@ const ( SandboxError = types.SandboxError SandboxDeleting = types.SandboxDeleting SandboxUnknown = types.SandboxUnknown + SandboxStopping = types.SandboxStopping + SandboxStopped = types.SandboxStopped + SandboxStarting = types.SandboxStarting ) // EventType classifies watch events. diff --git a/sdk/go/openshell/v1/types/types.go b/sdk/go/openshell/v1/types/types.go index 01da4ba9ca..53ccd94a1c 100644 --- a/sdk/go/openshell/v1/types/types.go +++ b/sdk/go/openshell/v1/types/types.go @@ -15,6 +15,9 @@ const ( SandboxError SandboxPhase = "Error" SandboxDeleting SandboxPhase = "Deleting" SandboxUnknown SandboxPhase = "Unknown" + SandboxStopping SandboxPhase = "Stopping" + SandboxStopped SandboxPhase = "Stopped" + SandboxStarting SandboxPhase = "Starting" ) // EventType classifies watch events. diff --git a/sdk/go/proto/openshellv1/openshell.pb.go b/sdk/go/proto/openshellv1/openshell.pb.go index a854c751a2..fdbcadea97 100644 --- a/sdk/go/proto/openshellv1/openshell.pb.go +++ b/sdk/go/proto/openshellv1/openshell.pb.go @@ -41,6 +41,9 @@ const ( 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 ) // Enum value maps for SandboxPhase. @@ -52,6 +55,9 @@ var ( 3: "SANDBOX_PHASE_ERROR", 4: "SANDBOX_PHASE_DELETING", 5: "SANDBOX_PHASE_UNKNOWN", + 6: "SANDBOX_PHASE_STOPPING", + 7: "SANDBOX_PHASE_STOPPED", + 8: "SANDBOX_PHASE_STARTING", } SandboxPhase_value = map[string]int32{ "SANDBOX_PHASE_UNSPECIFIED": 0, @@ -60,6 +66,9 @@ var ( "SANDBOX_PHASE_ERROR": 3, "SANDBOX_PHASE_DELETING": 4, "SANDBOX_PHASE_UNKNOWN": 5, + "SANDBOX_PHASE_STOPPING": 6, + "SANDBOX_PHASE_STOPPED": 7, + "SANDBOX_PHASE_STARTING": 8, } ) @@ -2128,6 +2137,116 @@ func (x *DeleteSandboxRequest) GetWorkspace() string { return "" } +// Stop sandbox request. +type StopSandboxRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Sandbox name (canonical lookup key). + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Workspace scope. Empty defaults to "default". + Workspace string `protobuf:"bytes,2,opt,name=workspace,proto3" json:"workspace,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StopSandboxRequest) Reset() { + *x = StopSandboxRequest{} + mi := &file_openshell_proto_msgTypes[27] + 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[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 StopSandboxRequest.ProtoReflect.Descriptor instead. +func (*StopSandboxRequest) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{27} +} + +func (x *StopSandboxRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *StopSandboxRequest) GetWorkspace() string { + if x != nil { + return x.Workspace + } + return "" +} + +// Start sandbox request. +type StartSandboxRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Sandbox name (canonical lookup key). + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Workspace scope. Empty defaults to "default". + Workspace string `protobuf:"bytes,2,opt,name=workspace,proto3" json:"workspace,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StartSandboxRequest) Reset() { + *x = StartSandboxRequest{} + mi := &file_openshell_proto_msgTypes[28] + 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[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 StartSandboxRequest.ProtoReflect.Descriptor instead. +func (*StartSandboxRequest) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{28} +} + +func (x *StartSandboxRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *StartSandboxRequest) GetWorkspace() string { + if x != nil { + return x.Workspace + } + return "" +} + // Sandbox response. type SandboxResponse struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -2138,7 +2257,7 @@ type SandboxResponse struct { func (x *SandboxResponse) Reset() { *x = SandboxResponse{} - mi := &file_openshell_proto_msgTypes[27] + mi := &file_openshell_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2150,7 +2269,7 @@ func (x *SandboxResponse) String() string { func (*SandboxResponse) ProtoMessage() {} func (x *SandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[27] + mi := &file_openshell_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2163,7 +2282,7 @@ func (x *SandboxResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxResponse.ProtoReflect.Descriptor instead. func (*SandboxResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{27} + return file_openshell_proto_rawDescGZIP(), []int{29} } func (x *SandboxResponse) GetSandbox() *Sandbox { @@ -2183,7 +2302,7 @@ type ListSandboxesResponse struct { func (x *ListSandboxesResponse) Reset() { *x = ListSandboxesResponse{} - mi := &file_openshell_proto_msgTypes[28] + mi := &file_openshell_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2195,7 +2314,7 @@ func (x *ListSandboxesResponse) String() string { func (*ListSandboxesResponse) ProtoMessage() {} func (x *ListSandboxesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[28] + mi := &file_openshell_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2208,7 +2327,7 @@ func (x *ListSandboxesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSandboxesResponse.ProtoReflect.Descriptor instead. func (*ListSandboxesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{28} + return file_openshell_proto_rawDescGZIP(), []int{30} } func (x *ListSandboxesResponse) GetSandboxes() []*Sandbox { @@ -2228,7 +2347,7 @@ type ListSandboxProvidersResponse struct { func (x *ListSandboxProvidersResponse) Reset() { *x = ListSandboxProvidersResponse{} - mi := &file_openshell_proto_msgTypes[29] + mi := &file_openshell_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2240,7 +2359,7 @@ func (x *ListSandboxProvidersResponse) String() string { func (*ListSandboxProvidersResponse) ProtoMessage() {} func (x *ListSandboxProvidersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[29] + mi := &file_openshell_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2253,7 +2372,7 @@ func (x *ListSandboxProvidersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSandboxProvidersResponse.ProtoReflect.Descriptor instead. func (*ListSandboxProvidersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{29} + return file_openshell_proto_rawDescGZIP(), []int{31} } func (x *ListSandboxProvidersResponse) GetProviders() []*datamodelv1.Provider { @@ -2275,7 +2394,7 @@ type AttachSandboxProviderResponse struct { func (x *AttachSandboxProviderResponse) Reset() { *x = AttachSandboxProviderResponse{} - mi := &file_openshell_proto_msgTypes[30] + mi := &file_openshell_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2287,7 +2406,7 @@ func (x *AttachSandboxProviderResponse) String() string { func (*AttachSandboxProviderResponse) ProtoMessage() {} func (x *AttachSandboxProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[30] + mi := &file_openshell_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2300,7 +2419,7 @@ func (x *AttachSandboxProviderResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AttachSandboxProviderResponse.ProtoReflect.Descriptor instead. func (*AttachSandboxProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{30} + return file_openshell_proto_rawDescGZIP(), []int{32} } func (x *AttachSandboxProviderResponse) GetSandbox() *Sandbox { @@ -2329,7 +2448,7 @@ type DetachSandboxProviderResponse struct { func (x *DetachSandboxProviderResponse) Reset() { *x = DetachSandboxProviderResponse{} - mi := &file_openshell_proto_msgTypes[31] + mi := &file_openshell_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2341,7 +2460,7 @@ func (x *DetachSandboxProviderResponse) String() string { func (*DetachSandboxProviderResponse) ProtoMessage() {} func (x *DetachSandboxProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[31] + mi := &file_openshell_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2354,7 +2473,7 @@ func (x *DetachSandboxProviderResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DetachSandboxProviderResponse.ProtoReflect.Descriptor instead. func (*DetachSandboxProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{31} + return file_openshell_proto_rawDescGZIP(), []int{33} } func (x *DetachSandboxProviderResponse) GetSandbox() *Sandbox { @@ -2381,7 +2500,7 @@ type DeleteSandboxResponse struct { func (x *DeleteSandboxResponse) Reset() { *x = DeleteSandboxResponse{} - mi := &file_openshell_proto_msgTypes[32] + mi := &file_openshell_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2393,7 +2512,7 @@ func (x *DeleteSandboxResponse) String() string { func (*DeleteSandboxResponse) ProtoMessage() {} func (x *DeleteSandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[32] + mi := &file_openshell_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2406,7 +2525,7 @@ func (x *DeleteSandboxResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteSandboxResponse.ProtoReflect.Descriptor instead. func (*DeleteSandboxResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{32} + return file_openshell_proto_rawDescGZIP(), []int{34} } func (x *DeleteSandboxResponse) GetDeleted() bool { @@ -2427,7 +2546,7 @@ type CreateSshSessionRequest struct { func (x *CreateSshSessionRequest) Reset() { *x = CreateSshSessionRequest{} - mi := &file_openshell_proto_msgTypes[33] + mi := &file_openshell_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2439,7 +2558,7 @@ func (x *CreateSshSessionRequest) String() string { func (*CreateSshSessionRequest) ProtoMessage() {} func (x *CreateSshSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[33] + mi := &file_openshell_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2452,7 +2571,7 @@ func (x *CreateSshSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateSshSessionRequest.ProtoReflect.Descriptor instead. func (*CreateSshSessionRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{33} + return file_openshell_proto_rawDescGZIP(), []int{35} } func (x *CreateSshSessionRequest) GetSandboxId() string { @@ -2495,7 +2614,7 @@ type CreateSshSessionResponse struct { func (x *CreateSshSessionResponse) Reset() { *x = CreateSshSessionResponse{} - mi := &file_openshell_proto_msgTypes[34] + mi := &file_openshell_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2507,7 +2626,7 @@ func (x *CreateSshSessionResponse) String() string { func (*CreateSshSessionResponse) ProtoMessage() {} func (x *CreateSshSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[34] + mi := &file_openshell_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2520,7 +2639,7 @@ func (x *CreateSshSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateSshSessionResponse.ProtoReflect.Descriptor instead. func (*CreateSshSessionResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{34} + return file_openshell_proto_rawDescGZIP(), []int{36} } func (x *CreateSshSessionResponse) GetSandboxId() string { @@ -2591,7 +2710,7 @@ type ExposeServiceRequest struct { func (x *ExposeServiceRequest) Reset() { *x = ExposeServiceRequest{} - mi := &file_openshell_proto_msgTypes[35] + mi := &file_openshell_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2603,7 +2722,7 @@ func (x *ExposeServiceRequest) String() string { func (*ExposeServiceRequest) ProtoMessage() {} func (x *ExposeServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[35] + mi := &file_openshell_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2616,7 +2735,7 @@ func (x *ExposeServiceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExposeServiceRequest.ProtoReflect.Descriptor instead. func (*ExposeServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{35} + return file_openshell_proto_rawDescGZIP(), []int{37} } func (x *ExposeServiceRequest) GetSandbox() string { @@ -2669,7 +2788,7 @@ type GetServiceRequest struct { func (x *GetServiceRequest) Reset() { *x = GetServiceRequest{} - mi := &file_openshell_proto_msgTypes[36] + mi := &file_openshell_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2681,7 +2800,7 @@ func (x *GetServiceRequest) String() string { func (*GetServiceRequest) ProtoMessage() {} func (x *GetServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[36] + mi := &file_openshell_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2694,7 +2813,7 @@ func (x *GetServiceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetServiceRequest.ProtoReflect.Descriptor instead. func (*GetServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{36} + return file_openshell_proto_rawDescGZIP(), []int{38} } func (x *GetServiceRequest) GetSandbox() string { @@ -2737,7 +2856,7 @@ type ListServicesRequest struct { func (x *ListServicesRequest) Reset() { *x = ListServicesRequest{} - mi := &file_openshell_proto_msgTypes[37] + mi := &file_openshell_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2749,7 +2868,7 @@ func (x *ListServicesRequest) String() string { func (*ListServicesRequest) ProtoMessage() {} func (x *ListServicesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[37] + mi := &file_openshell_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2762,7 +2881,7 @@ func (x *ListServicesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListServicesRequest.ProtoReflect.Descriptor instead. func (*ListServicesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{37} + return file_openshell_proto_rawDescGZIP(), []int{39} } func (x *ListServicesRequest) GetSandbox() string { @@ -2810,7 +2929,7 @@ type ListServicesResponse struct { func (x *ListServicesResponse) Reset() { *x = ListServicesResponse{} - mi := &file_openshell_proto_msgTypes[38] + mi := &file_openshell_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2822,7 +2941,7 @@ func (x *ListServicesResponse) String() string { func (*ListServicesResponse) ProtoMessage() {} func (x *ListServicesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[38] + mi := &file_openshell_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2835,7 +2954,7 @@ func (x *ListServicesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListServicesResponse.ProtoReflect.Descriptor instead. func (*ListServicesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{38} + return file_openshell_proto_rawDescGZIP(), []int{40} } func (x *ListServicesResponse) GetServices() []*ServiceEndpointResponse { @@ -2860,7 +2979,7 @@ type DeleteServiceRequest struct { func (x *DeleteServiceRequest) Reset() { *x = DeleteServiceRequest{} - mi := &file_openshell_proto_msgTypes[39] + mi := &file_openshell_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2872,7 +2991,7 @@ func (x *DeleteServiceRequest) String() string { func (*DeleteServiceRequest) ProtoMessage() {} func (x *DeleteServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[39] + mi := &file_openshell_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2885,7 +3004,7 @@ func (x *DeleteServiceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteServiceRequest.ProtoReflect.Descriptor instead. func (*DeleteServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{39} + return file_openshell_proto_rawDescGZIP(), []int{41} } func (x *DeleteServiceRequest) GetSandbox() string { @@ -2920,7 +3039,7 @@ type DeleteServiceResponse struct { func (x *DeleteServiceResponse) Reset() { *x = DeleteServiceResponse{} - mi := &file_openshell_proto_msgTypes[40] + mi := &file_openshell_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2932,7 +3051,7 @@ func (x *DeleteServiceResponse) String() string { func (*DeleteServiceResponse) ProtoMessage() {} func (x *DeleteServiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[40] + mi := &file_openshell_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2945,7 +3064,7 @@ func (x *DeleteServiceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteServiceResponse.ProtoReflect.Descriptor instead. func (*DeleteServiceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{40} + return file_openshell_proto_rawDescGZIP(), []int{42} } func (x *DeleteServiceResponse) GetDeleted() bool { @@ -2976,7 +3095,7 @@ type ServiceEndpoint struct { func (x *ServiceEndpoint) Reset() { *x = ServiceEndpoint{} - mi := &file_openshell_proto_msgTypes[41] + mi := &file_openshell_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2988,7 +3107,7 @@ func (x *ServiceEndpoint) String() string { func (*ServiceEndpoint) ProtoMessage() {} func (x *ServiceEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[41] + mi := &file_openshell_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3001,7 +3120,7 @@ func (x *ServiceEndpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use ServiceEndpoint.ProtoReflect.Descriptor instead. func (*ServiceEndpoint) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{41} + return file_openshell_proto_rawDescGZIP(), []int{43} } func (x *ServiceEndpoint) GetMetadata() *datamodelv1.ObjectMeta { @@ -3057,7 +3176,7 @@ type ServiceEndpointResponse struct { func (x *ServiceEndpointResponse) Reset() { *x = ServiceEndpointResponse{} - mi := &file_openshell_proto_msgTypes[42] + mi := &file_openshell_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3069,7 +3188,7 @@ func (x *ServiceEndpointResponse) String() string { func (*ServiceEndpointResponse) ProtoMessage() {} func (x *ServiceEndpointResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[42] + mi := &file_openshell_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3082,7 +3201,7 @@ func (x *ServiceEndpointResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ServiceEndpointResponse.ProtoReflect.Descriptor instead. func (*ServiceEndpointResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{42} + return file_openshell_proto_rawDescGZIP(), []int{44} } func (x *ServiceEndpointResponse) GetEndpoint() *ServiceEndpoint { @@ -3110,7 +3229,7 @@ type RevokeSshSessionRequest struct { func (x *RevokeSshSessionRequest) Reset() { *x = RevokeSshSessionRequest{} - mi := &file_openshell_proto_msgTypes[43] + mi := &file_openshell_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3122,7 +3241,7 @@ func (x *RevokeSshSessionRequest) String() string { func (*RevokeSshSessionRequest) ProtoMessage() {} func (x *RevokeSshSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[43] + mi := &file_openshell_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3135,7 +3254,7 @@ func (x *RevokeSshSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RevokeSshSessionRequest.ProtoReflect.Descriptor instead. func (*RevokeSshSessionRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{43} + return file_openshell_proto_rawDescGZIP(), []int{45} } func (x *RevokeSshSessionRequest) GetToken() string { @@ -3156,7 +3275,7 @@ type RevokeSshSessionResponse struct { func (x *RevokeSshSessionResponse) Reset() { *x = RevokeSshSessionResponse{} - mi := &file_openshell_proto_msgTypes[44] + mi := &file_openshell_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3168,7 +3287,7 @@ func (x *RevokeSshSessionResponse) String() string { func (*RevokeSshSessionResponse) ProtoMessage() {} func (x *RevokeSshSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[44] + mi := &file_openshell_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3181,7 +3300,7 @@ func (x *RevokeSshSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RevokeSshSessionResponse.ProtoReflect.Descriptor instead. func (*RevokeSshSessionResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{44} + return file_openshell_proto_rawDescGZIP(), []int{46} } func (x *RevokeSshSessionResponse) GetRevoked() bool { @@ -3218,7 +3337,7 @@ type ExecSandboxRequest struct { func (x *ExecSandboxRequest) Reset() { *x = ExecSandboxRequest{} - mi := &file_openshell_proto_msgTypes[45] + mi := &file_openshell_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3230,7 +3349,7 @@ func (x *ExecSandboxRequest) String() string { func (*ExecSandboxRequest) ProtoMessage() {} func (x *ExecSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[45] + mi := &file_openshell_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3243,7 +3362,7 @@ func (x *ExecSandboxRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecSandboxRequest.ProtoReflect.Descriptor instead. func (*ExecSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{45} + return file_openshell_proto_rawDescGZIP(), []int{47} } func (x *ExecSandboxRequest) GetSandboxId() string { @@ -3319,7 +3438,7 @@ type ExecSandboxStdout struct { func (x *ExecSandboxStdout) Reset() { *x = ExecSandboxStdout{} - mi := &file_openshell_proto_msgTypes[46] + mi := &file_openshell_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3331,7 +3450,7 @@ func (x *ExecSandboxStdout) String() string { func (*ExecSandboxStdout) ProtoMessage() {} func (x *ExecSandboxStdout) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[46] + mi := &file_openshell_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3344,7 +3463,7 @@ func (x *ExecSandboxStdout) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecSandboxStdout.ProtoReflect.Descriptor instead. func (*ExecSandboxStdout) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{46} + return file_openshell_proto_rawDescGZIP(), []int{48} } func (x *ExecSandboxStdout) GetData() []byte { @@ -3364,7 +3483,7 @@ type ExecSandboxStderr struct { func (x *ExecSandboxStderr) Reset() { *x = ExecSandboxStderr{} - mi := &file_openshell_proto_msgTypes[47] + mi := &file_openshell_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3376,7 +3495,7 @@ func (x *ExecSandboxStderr) String() string { func (*ExecSandboxStderr) ProtoMessage() {} func (x *ExecSandboxStderr) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[47] + mi := &file_openshell_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3389,7 +3508,7 @@ func (x *ExecSandboxStderr) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecSandboxStderr.ProtoReflect.Descriptor instead. func (*ExecSandboxStderr) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{47} + return file_openshell_proto_rawDescGZIP(), []int{49} } func (x *ExecSandboxStderr) GetData() []byte { @@ -3409,7 +3528,7 @@ type ExecSandboxExit struct { func (x *ExecSandboxExit) Reset() { *x = ExecSandboxExit{} - mi := &file_openshell_proto_msgTypes[48] + mi := &file_openshell_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3421,7 +3540,7 @@ func (x *ExecSandboxExit) String() string { func (*ExecSandboxExit) ProtoMessage() {} func (x *ExecSandboxExit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[48] + mi := &file_openshell_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3434,7 +3553,7 @@ func (x *ExecSandboxExit) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecSandboxExit.ProtoReflect.Descriptor instead. func (*ExecSandboxExit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{48} + return file_openshell_proto_rawDescGZIP(), []int{50} } func (x *ExecSandboxExit) GetExitCode() int32 { @@ -3459,7 +3578,7 @@ type ExecSandboxEvent struct { func (x *ExecSandboxEvent) Reset() { *x = ExecSandboxEvent{} - mi := &file_openshell_proto_msgTypes[49] + mi := &file_openshell_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3471,7 +3590,7 @@ func (x *ExecSandboxEvent) String() string { func (*ExecSandboxEvent) ProtoMessage() {} func (x *ExecSandboxEvent) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[49] + mi := &file_openshell_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3484,7 +3603,7 @@ func (x *ExecSandboxEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecSandboxEvent.ProtoReflect.Descriptor instead. func (*ExecSandboxEvent) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{49} + return file_openshell_proto_rawDescGZIP(), []int{51} } func (x *ExecSandboxEvent) GetPayload() isExecSandboxEvent_Payload { @@ -3566,7 +3685,7 @@ type TcpForwardInit struct { func (x *TcpForwardInit) Reset() { *x = TcpForwardInit{} - mi := &file_openshell_proto_msgTypes[50] + mi := &file_openshell_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3578,7 +3697,7 @@ func (x *TcpForwardInit) String() string { func (*TcpForwardInit) ProtoMessage() {} func (x *TcpForwardInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[50] + mi := &file_openshell_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3591,7 +3710,7 @@ func (x *TcpForwardInit) ProtoReflect() protoreflect.Message { // Deprecated: Use TcpForwardInit.ProtoReflect.Descriptor instead. func (*TcpForwardInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{50} + return file_openshell_proto_rawDescGZIP(), []int{52} } func (x *TcpForwardInit) GetSandboxId() string { @@ -3670,7 +3789,7 @@ type TcpForwardFrame struct { func (x *TcpForwardFrame) Reset() { *x = TcpForwardFrame{} - mi := &file_openshell_proto_msgTypes[51] + mi := &file_openshell_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3682,7 +3801,7 @@ func (x *TcpForwardFrame) String() string { func (*TcpForwardFrame) ProtoMessage() {} func (x *TcpForwardFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[51] + mi := &file_openshell_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3695,7 +3814,7 @@ func (x *TcpForwardFrame) ProtoReflect() protoreflect.Message { // Deprecated: Use TcpForwardFrame.ProtoReflect.Descriptor instead. func (*TcpForwardFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{51} + return file_openshell_proto_rawDescGZIP(), []int{53} } func (x *TcpForwardFrame) GetPayload() isTcpForwardFrame_Payload { @@ -3754,7 +3873,7 @@ type ExecSandboxInput struct { func (x *ExecSandboxInput) Reset() { *x = ExecSandboxInput{} - mi := &file_openshell_proto_msgTypes[52] + mi := &file_openshell_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3766,7 +3885,7 @@ func (x *ExecSandboxInput) String() string { func (*ExecSandboxInput) ProtoMessage() {} func (x *ExecSandboxInput) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[52] + mi := &file_openshell_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3779,7 +3898,7 @@ func (x *ExecSandboxInput) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecSandboxInput.ProtoReflect.Descriptor instead. func (*ExecSandboxInput) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{52} + return file_openshell_proto_rawDescGZIP(), []int{54} } func (x *ExecSandboxInput) GetPayload() isExecSandboxInput_Payload { @@ -3852,7 +3971,7 @@ type ExecSandboxWindowResize struct { func (x *ExecSandboxWindowResize) Reset() { *x = ExecSandboxWindowResize{} - mi := &file_openshell_proto_msgTypes[53] + mi := &file_openshell_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3864,7 +3983,7 @@ func (x *ExecSandboxWindowResize) String() string { func (*ExecSandboxWindowResize) ProtoMessage() {} func (x *ExecSandboxWindowResize) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[53] + mi := &file_openshell_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3877,7 +3996,7 @@ func (x *ExecSandboxWindowResize) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecSandboxWindowResize.ProtoReflect.Descriptor instead. func (*ExecSandboxWindowResize) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{53} + return file_openshell_proto_rawDescGZIP(), []int{55} } func (x *ExecSandboxWindowResize) GetCols() uint32 { @@ -3914,7 +4033,7 @@ type SshSession struct { func (x *SshSession) Reset() { *x = SshSession{} - mi := &file_openshell_proto_msgTypes[54] + mi := &file_openshell_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3926,7 +4045,7 @@ func (x *SshSession) String() string { func (*SshSession) ProtoMessage() {} func (x *SshSession) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[54] + mi := &file_openshell_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3939,7 +4058,7 @@ func (x *SshSession) ProtoReflect() protoreflect.Message { // Deprecated: Use SshSession.ProtoReflect.Descriptor instead. func (*SshSession) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{54} + return file_openshell_proto_rawDescGZIP(), []int{56} } func (x *SshSession) GetMetadata() *datamodelv1.ObjectMeta { @@ -4007,7 +4126,7 @@ type WatchSandboxRequest struct { func (x *WatchSandboxRequest) Reset() { *x = WatchSandboxRequest{} - mi := &file_openshell_proto_msgTypes[55] + mi := &file_openshell_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4019,7 +4138,7 @@ func (x *WatchSandboxRequest) String() string { func (*WatchSandboxRequest) ProtoMessage() {} func (x *WatchSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[55] + mi := &file_openshell_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4032,7 +4151,7 @@ func (x *WatchSandboxRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSandboxRequest.ProtoReflect.Descriptor instead. func (*WatchSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{55} + return file_openshell_proto_rawDescGZIP(), []int{57} } func (x *WatchSandboxRequest) GetId() string { @@ -4122,7 +4241,7 @@ type SandboxStreamEvent struct { func (x *SandboxStreamEvent) Reset() { *x = SandboxStreamEvent{} - mi := &file_openshell_proto_msgTypes[56] + mi := &file_openshell_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4134,7 +4253,7 @@ func (x *SandboxStreamEvent) String() string { func (*SandboxStreamEvent) ProtoMessage() {} func (x *SandboxStreamEvent) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[56] + mi := &file_openshell_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4147,7 +4266,7 @@ func (x *SandboxStreamEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxStreamEvent.ProtoReflect.Descriptor instead. func (*SandboxStreamEvent) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{56} + return file_openshell_proto_rawDescGZIP(), []int{58} } func (x *SandboxStreamEvent) GetPayload() isSandboxStreamEvent_Payload { @@ -4260,7 +4379,7 @@ type SandboxLogLine struct { func (x *SandboxLogLine) Reset() { *x = SandboxLogLine{} - mi := &file_openshell_proto_msgTypes[57] + mi := &file_openshell_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4272,7 +4391,7 @@ func (x *SandboxLogLine) String() string { func (*SandboxLogLine) ProtoMessage() {} func (x *SandboxLogLine) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[57] + mi := &file_openshell_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4285,7 +4404,7 @@ func (x *SandboxLogLine) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxLogLine.ProtoReflect.Descriptor instead. func (*SandboxLogLine) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{57} + return file_openshell_proto_rawDescGZIP(), []int{59} } func (x *SandboxLogLine) GetSandboxId() string { @@ -4346,7 +4465,7 @@ type SandboxStreamWarning struct { func (x *SandboxStreamWarning) Reset() { *x = SandboxStreamWarning{} - mi := &file_openshell_proto_msgTypes[58] + mi := &file_openshell_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4358,7 +4477,7 @@ func (x *SandboxStreamWarning) String() string { func (*SandboxStreamWarning) ProtoMessage() {} func (x *SandboxStreamWarning) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[58] + mi := &file_openshell_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4371,7 +4490,7 @@ func (x *SandboxStreamWarning) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxStreamWarning.ProtoReflect.Descriptor instead. func (*SandboxStreamWarning) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{58} + return file_openshell_proto_rawDescGZIP(), []int{60} } func (x *SandboxStreamWarning) GetMessage() string { @@ -4393,7 +4512,7 @@ type CreateProviderRequest struct { func (x *CreateProviderRequest) Reset() { *x = CreateProviderRequest{} - mi := &file_openshell_proto_msgTypes[59] + mi := &file_openshell_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4405,7 +4524,7 @@ func (x *CreateProviderRequest) String() string { func (*CreateProviderRequest) ProtoMessage() {} func (x *CreateProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[59] + mi := &file_openshell_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4418,7 +4537,7 @@ func (x *CreateProviderRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateProviderRequest.ProtoReflect.Descriptor instead. func (*CreateProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{59} + return file_openshell_proto_rawDescGZIP(), []int{61} } func (x *CreateProviderRequest) GetProvider() *datamodelv1.Provider { @@ -4447,7 +4566,7 @@ type GetProviderRequest struct { func (x *GetProviderRequest) Reset() { *x = GetProviderRequest{} - mi := &file_openshell_proto_msgTypes[60] + mi := &file_openshell_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4459,7 +4578,7 @@ func (x *GetProviderRequest) String() string { func (*GetProviderRequest) ProtoMessage() {} func (x *GetProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[60] + mi := &file_openshell_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4472,7 +4591,7 @@ func (x *GetProviderRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetProviderRequest.ProtoReflect.Descriptor instead. func (*GetProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{60} + return file_openshell_proto_rawDescGZIP(), []int{62} } func (x *GetProviderRequest) GetName() string { @@ -4504,7 +4623,7 @@ type ListProvidersRequest struct { func (x *ListProvidersRequest) Reset() { *x = ListProvidersRequest{} - mi := &file_openshell_proto_msgTypes[61] + mi := &file_openshell_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4516,7 +4635,7 @@ func (x *ListProvidersRequest) String() string { func (*ListProvidersRequest) ProtoMessage() {} func (x *ListProvidersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[61] + mi := &file_openshell_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4529,7 +4648,7 @@ func (x *ListProvidersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListProvidersRequest.ProtoReflect.Descriptor instead. func (*ListProvidersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{61} + return file_openshell_proto_rawDescGZIP(), []int{63} } func (x *ListProvidersRequest) GetLimit() uint32 { @@ -4575,7 +4694,7 @@ type UpdateProviderRequest struct { func (x *UpdateProviderRequest) Reset() { *x = UpdateProviderRequest{} - mi := &file_openshell_proto_msgTypes[62] + mi := &file_openshell_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4587,7 +4706,7 @@ func (x *UpdateProviderRequest) String() string { func (*UpdateProviderRequest) ProtoMessage() {} func (x *UpdateProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[62] + mi := &file_openshell_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4600,7 +4719,7 @@ func (x *UpdateProviderRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateProviderRequest.ProtoReflect.Descriptor instead. func (*UpdateProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{62} + return file_openshell_proto_rawDescGZIP(), []int{64} } func (x *UpdateProviderRequest) GetProvider() *datamodelv1.Provider { @@ -4636,7 +4755,7 @@ type DeleteProviderRequest struct { func (x *DeleteProviderRequest) Reset() { *x = DeleteProviderRequest{} - mi := &file_openshell_proto_msgTypes[63] + mi := &file_openshell_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4648,7 +4767,7 @@ func (x *DeleteProviderRequest) String() string { func (*DeleteProviderRequest) ProtoMessage() {} func (x *DeleteProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[63] + mi := &file_openshell_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4661,7 +4780,7 @@ func (x *DeleteProviderRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteProviderRequest.ProtoReflect.Descriptor instead. func (*DeleteProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{63} + return file_openshell_proto_rawDescGZIP(), []int{65} } func (x *DeleteProviderRequest) GetName() string { @@ -4688,7 +4807,7 @@ type ProviderResponse struct { func (x *ProviderResponse) Reset() { *x = ProviderResponse{} - mi := &file_openshell_proto_msgTypes[64] + mi := &file_openshell_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4700,7 +4819,7 @@ func (x *ProviderResponse) String() string { func (*ProviderResponse) ProtoMessage() {} func (x *ProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[64] + mi := &file_openshell_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4713,7 +4832,7 @@ func (x *ProviderResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderResponse.ProtoReflect.Descriptor instead. func (*ProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{64} + return file_openshell_proto_rawDescGZIP(), []int{66} } func (x *ProviderResponse) GetProvider() *datamodelv1.Provider { @@ -4733,7 +4852,7 @@ type ListProvidersResponse struct { func (x *ListProvidersResponse) Reset() { *x = ListProvidersResponse{} - mi := &file_openshell_proto_msgTypes[65] + mi := &file_openshell_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4745,7 +4864,7 @@ func (x *ListProvidersResponse) String() string { func (*ListProvidersResponse) ProtoMessage() {} func (x *ListProvidersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[65] + mi := &file_openshell_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4758,7 +4877,7 @@ func (x *ListProvidersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListProvidersResponse.ProtoReflect.Descriptor instead. func (*ListProvidersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{65} + return file_openshell_proto_rawDescGZIP(), []int{67} } func (x *ListProvidersResponse) GetProviders() []*datamodelv1.Provider { @@ -4782,7 +4901,7 @@ type ListProviderProfilesRequest struct { func (x *ListProviderProfilesRequest) Reset() { *x = ListProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[66] + mi := &file_openshell_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4794,7 +4913,7 @@ func (x *ListProviderProfilesRequest) String() string { func (*ListProviderProfilesRequest) ProtoMessage() {} func (x *ListProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[66] + mi := &file_openshell_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4807,7 +4926,7 @@ func (x *ListProviderProfilesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListProviderProfilesRequest.ProtoReflect.Descriptor instead. func (*ListProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{66} + return file_openshell_proto_rawDescGZIP(), []int{68} } func (x *ListProviderProfilesRequest) GetLimit() uint32 { @@ -4845,7 +4964,7 @@ type GetProviderProfileRequest struct { func (x *GetProviderProfileRequest) Reset() { *x = GetProviderProfileRequest{} - mi := &file_openshell_proto_msgTypes[67] + mi := &file_openshell_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4857,7 +4976,7 @@ func (x *GetProviderProfileRequest) String() string { func (*GetProviderProfileRequest) ProtoMessage() {} func (x *GetProviderProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[67] + mi := &file_openshell_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4870,7 +4989,7 @@ func (x *GetProviderProfileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetProviderProfileRequest.ProtoReflect.Descriptor instead. func (*GetProviderProfileRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{67} + return file_openshell_proto_rawDescGZIP(), []int{69} } func (x *GetProviderProfileRequest) GetId() string { @@ -4898,7 +5017,7 @@ type ProviderProfileImportItem struct { func (x *ProviderProfileImportItem) Reset() { *x = ProviderProfileImportItem{} - mi := &file_openshell_proto_msgTypes[68] + mi := &file_openshell_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4910,7 +5029,7 @@ func (x *ProviderProfileImportItem) String() string { func (*ProviderProfileImportItem) ProtoMessage() {} func (x *ProviderProfileImportItem) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[68] + mi := &file_openshell_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4923,7 +5042,7 @@ func (x *ProviderProfileImportItem) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderProfileImportItem.ProtoReflect.Descriptor instead. func (*ProviderProfileImportItem) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{68} + return file_openshell_proto_rawDescGZIP(), []int{70} } func (x *ProviderProfileImportItem) GetProfile() *ProviderProfile { @@ -4954,7 +5073,7 @@ type ProviderProfileDiagnostic struct { func (x *ProviderProfileDiagnostic) Reset() { *x = ProviderProfileDiagnostic{} - mi := &file_openshell_proto_msgTypes[69] + mi := &file_openshell_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4966,7 +5085,7 @@ func (x *ProviderProfileDiagnostic) String() string { func (*ProviderProfileDiagnostic) ProtoMessage() {} func (x *ProviderProfileDiagnostic) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[69] + mi := &file_openshell_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4979,7 +5098,7 @@ func (x *ProviderProfileDiagnostic) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderProfileDiagnostic.ProtoReflect.Descriptor instead. func (*ProviderProfileDiagnostic) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{69} + return file_openshell_proto_rawDescGZIP(), []int{71} } func (x *ProviderProfileDiagnostic) GetSource() string { @@ -5036,7 +5155,7 @@ type ProviderCredentialTokenGrantAudienceOverride struct { func (x *ProviderCredentialTokenGrantAudienceOverride) Reset() { *x = ProviderCredentialTokenGrantAudienceOverride{} - mi := &file_openshell_proto_msgTypes[70] + mi := &file_openshell_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5048,7 +5167,7 @@ func (x *ProviderCredentialTokenGrantAudienceOverride) String() string { func (*ProviderCredentialTokenGrantAudienceOverride) ProtoMessage() {} func (x *ProviderCredentialTokenGrantAudienceOverride) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[70] + mi := &file_openshell_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5061,7 +5180,7 @@ func (x *ProviderCredentialTokenGrantAudienceOverride) ProtoReflect() protorefle // Deprecated: Use ProviderCredentialTokenGrantAudienceOverride.ProtoReflect.Descriptor instead. func (*ProviderCredentialTokenGrantAudienceOverride) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{70} + return file_openshell_proto_rawDescGZIP(), []int{72} } func (x *ProviderCredentialTokenGrantAudienceOverride) GetHost() string { @@ -5126,7 +5245,7 @@ type ProviderCredentialTokenGrant struct { func (x *ProviderCredentialTokenGrant) Reset() { *x = ProviderCredentialTokenGrant{} - mi := &file_openshell_proto_msgTypes[71] + mi := &file_openshell_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5138,7 +5257,7 @@ func (x *ProviderCredentialTokenGrant) String() string { func (*ProviderCredentialTokenGrant) ProtoMessage() {} func (x *ProviderCredentialTokenGrant) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[71] + mi := &file_openshell_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5151,7 +5270,7 @@ func (x *ProviderCredentialTokenGrant) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderCredentialTokenGrant.ProtoReflect.Descriptor instead. func (*ProviderCredentialTokenGrant) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{71} + return file_openshell_proto_rawDescGZIP(), []int{73} } func (x *ProviderCredentialTokenGrant) GetTokenEndpoint() string { @@ -5222,7 +5341,7 @@ type ProviderProfileCredential struct { func (x *ProviderProfileCredential) Reset() { *x = ProviderProfileCredential{} - mi := &file_openshell_proto_msgTypes[72] + mi := &file_openshell_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5234,7 +5353,7 @@ func (x *ProviderProfileCredential) String() string { func (*ProviderProfileCredential) ProtoMessage() {} func (x *ProviderProfileCredential) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[72] + mi := &file_openshell_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5247,7 +5366,7 @@ func (x *ProviderProfileCredential) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderProfileCredential.ProtoReflect.Descriptor instead. func (*ProviderProfileCredential) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{72} + return file_openshell_proto_rawDescGZIP(), []int{74} } func (x *ProviderProfileCredential) GetName() string { @@ -5332,7 +5451,7 @@ type ProviderCredentialRefreshMaterial struct { func (x *ProviderCredentialRefreshMaterial) Reset() { *x = ProviderCredentialRefreshMaterial{} - mi := &file_openshell_proto_msgTypes[73] + mi := &file_openshell_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5344,7 +5463,7 @@ func (x *ProviderCredentialRefreshMaterial) String() string { func (*ProviderCredentialRefreshMaterial) ProtoMessage() {} func (x *ProviderCredentialRefreshMaterial) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[73] + mi := &file_openshell_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5357,7 +5476,7 @@ func (x *ProviderCredentialRefreshMaterial) ProtoReflect() protoreflect.Message // Deprecated: Use ProviderCredentialRefreshMaterial.ProtoReflect.Descriptor instead. func (*ProviderCredentialRefreshMaterial) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{73} + return file_openshell_proto_rawDescGZIP(), []int{75} } func (x *ProviderCredentialRefreshMaterial) GetName() string { @@ -5402,7 +5521,7 @@ type ProviderCredentialRefreshOutput struct { func (x *ProviderCredentialRefreshOutput) Reset() { *x = ProviderCredentialRefreshOutput{} - mi := &file_openshell_proto_msgTypes[74] + mi := &file_openshell_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5414,7 +5533,7 @@ func (x *ProviderCredentialRefreshOutput) String() string { func (*ProviderCredentialRefreshOutput) ProtoMessage() {} func (x *ProviderCredentialRefreshOutput) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[74] + mi := &file_openshell_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5427,7 +5546,7 @@ func (x *ProviderCredentialRefreshOutput) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderCredentialRefreshOutput.ProtoReflect.Descriptor instead. func (*ProviderCredentialRefreshOutput) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{74} + return file_openshell_proto_rawDescGZIP(), []int{76} } func (x *ProviderCredentialRefreshOutput) GetOutput() string { @@ -5459,7 +5578,7 @@ type ProviderCredentialRefresh struct { func (x *ProviderCredentialRefresh) Reset() { *x = ProviderCredentialRefresh{} - mi := &file_openshell_proto_msgTypes[75] + mi := &file_openshell_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5471,7 +5590,7 @@ func (x *ProviderCredentialRefresh) String() string { func (*ProviderCredentialRefresh) ProtoMessage() {} func (x *ProviderCredentialRefresh) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[75] + mi := &file_openshell_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5484,7 +5603,7 @@ func (x *ProviderCredentialRefresh) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderCredentialRefresh.ProtoReflect.Descriptor instead. func (*ProviderCredentialRefresh) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{75} + return file_openshell_proto_rawDescGZIP(), []int{77} } func (x *ProviderCredentialRefresh) GetStrategy() ProviderCredentialRefreshStrategy { @@ -5553,7 +5672,7 @@ type ProviderCredentialRefreshStatus struct { func (x *ProviderCredentialRefreshStatus) Reset() { *x = ProviderCredentialRefreshStatus{} - mi := &file_openshell_proto_msgTypes[76] + mi := &file_openshell_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5565,7 +5684,7 @@ func (x *ProviderCredentialRefreshStatus) String() string { func (*ProviderCredentialRefreshStatus) ProtoMessage() {} func (x *ProviderCredentialRefreshStatus) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[76] + mi := &file_openshell_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5578,7 +5697,7 @@ func (x *ProviderCredentialRefreshStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderCredentialRefreshStatus.ProtoReflect.Descriptor instead. func (*ProviderCredentialRefreshStatus) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{76} + return file_openshell_proto_rawDescGZIP(), []int{78} } func (x *ProviderCredentialRefreshStatus) GetProviderName() string { @@ -5655,7 +5774,7 @@ type ProviderProfileDiscovery struct { func (x *ProviderProfileDiscovery) Reset() { *x = ProviderProfileDiscovery{} - mi := &file_openshell_proto_msgTypes[77] + mi := &file_openshell_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5667,7 +5786,7 @@ func (x *ProviderProfileDiscovery) String() string { func (*ProviderProfileDiscovery) ProtoMessage() {} func (x *ProviderProfileDiscovery) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[77] + mi := &file_openshell_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5680,7 +5799,7 @@ func (x *ProviderProfileDiscovery) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderProfileDiscovery.ProtoReflect.Descriptor instead. func (*ProviderProfileDiscovery) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{77} + return file_openshell_proto_rawDescGZIP(), []int{79} } func (x *ProviderProfileDiscovery) GetCredentials() []string { @@ -5719,7 +5838,7 @@ type StoredProviderCredentialRefreshState struct { func (x *StoredProviderCredentialRefreshState) Reset() { *x = StoredProviderCredentialRefreshState{} - mi := &file_openshell_proto_msgTypes[78] + mi := &file_openshell_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5731,7 +5850,7 @@ func (x *StoredProviderCredentialRefreshState) String() string { func (*StoredProviderCredentialRefreshState) ProtoMessage() {} func (x *StoredProviderCredentialRefreshState) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[78] + mi := &file_openshell_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5744,7 +5863,7 @@ func (x *StoredProviderCredentialRefreshState) ProtoReflect() protoreflect.Messa // Deprecated: Use StoredProviderCredentialRefreshState.ProtoReflect.Descriptor instead. func (*StoredProviderCredentialRefreshState) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{78} + return file_openshell_proto_rawDescGZIP(), []int{80} } func (x *StoredProviderCredentialRefreshState) GetMetadata() *datamodelv1.ObjectMeta { @@ -5878,7 +5997,7 @@ type GetProviderRefreshStatusRequest struct { func (x *GetProviderRefreshStatusRequest) Reset() { *x = GetProviderRefreshStatusRequest{} - mi := &file_openshell_proto_msgTypes[79] + mi := &file_openshell_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5890,7 +6009,7 @@ func (x *GetProviderRefreshStatusRequest) String() string { func (*GetProviderRefreshStatusRequest) ProtoMessage() {} func (x *GetProviderRefreshStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[79] + mi := &file_openshell_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5903,7 +6022,7 @@ func (x *GetProviderRefreshStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetProviderRefreshStatusRequest.ProtoReflect.Descriptor instead. func (*GetProviderRefreshStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{79} + return file_openshell_proto_rawDescGZIP(), []int{81} } func (x *GetProviderRefreshStatusRequest) GetProvider() string { @@ -5936,7 +6055,7 @@ type GetProviderRefreshStatusResponse struct { func (x *GetProviderRefreshStatusResponse) Reset() { *x = GetProviderRefreshStatusResponse{} - mi := &file_openshell_proto_msgTypes[80] + mi := &file_openshell_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5948,7 +6067,7 @@ func (x *GetProviderRefreshStatusResponse) String() string { func (*GetProviderRefreshStatusResponse) ProtoMessage() {} func (x *GetProviderRefreshStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[80] + mi := &file_openshell_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5961,7 +6080,7 @@ func (x *GetProviderRefreshStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetProviderRefreshStatusResponse.ProtoReflect.Descriptor instead. func (*GetProviderRefreshStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{80} + return file_openshell_proto_rawDescGZIP(), []int{82} } func (x *GetProviderRefreshStatusResponse) GetCredentials() []*ProviderCredentialRefreshStatus { @@ -5987,7 +6106,7 @@ type ConfigureProviderRefreshRequest struct { func (x *ConfigureProviderRefreshRequest) Reset() { *x = ConfigureProviderRefreshRequest{} - mi := &file_openshell_proto_msgTypes[81] + mi := &file_openshell_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5999,7 +6118,7 @@ func (x *ConfigureProviderRefreshRequest) String() string { func (*ConfigureProviderRefreshRequest) ProtoMessage() {} func (x *ConfigureProviderRefreshRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[81] + mi := &file_openshell_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6012,7 +6131,7 @@ func (x *ConfigureProviderRefreshRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ConfigureProviderRefreshRequest.ProtoReflect.Descriptor instead. func (*ConfigureProviderRefreshRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{81} + return file_openshell_proto_rawDescGZIP(), []int{83} } func (x *ConfigureProviderRefreshRequest) GetProvider() string { @@ -6073,7 +6192,7 @@ type ConfigureProviderRefreshResponse struct { func (x *ConfigureProviderRefreshResponse) Reset() { *x = ConfigureProviderRefreshResponse{} - mi := &file_openshell_proto_msgTypes[82] + mi := &file_openshell_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6085,7 +6204,7 @@ func (x *ConfigureProviderRefreshResponse) String() string { func (*ConfigureProviderRefreshResponse) ProtoMessage() {} func (x *ConfigureProviderRefreshResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[82] + mi := &file_openshell_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6098,7 +6217,7 @@ func (x *ConfigureProviderRefreshResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ConfigureProviderRefreshResponse.ProtoReflect.Descriptor instead. func (*ConfigureProviderRefreshResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{82} + return file_openshell_proto_rawDescGZIP(), []int{84} } func (x *ConfigureProviderRefreshResponse) GetStatus() *ProviderCredentialRefreshStatus { @@ -6120,7 +6239,7 @@ type RotateProviderCredentialRequest struct { func (x *RotateProviderCredentialRequest) Reset() { *x = RotateProviderCredentialRequest{} - mi := &file_openshell_proto_msgTypes[83] + mi := &file_openshell_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6132,7 +6251,7 @@ func (x *RotateProviderCredentialRequest) String() string { func (*RotateProviderCredentialRequest) ProtoMessage() {} func (x *RotateProviderCredentialRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[83] + mi := &file_openshell_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6145,7 +6264,7 @@ func (x *RotateProviderCredentialRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RotateProviderCredentialRequest.ProtoReflect.Descriptor instead. func (*RotateProviderCredentialRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{83} + return file_openshell_proto_rawDescGZIP(), []int{85} } func (x *RotateProviderCredentialRequest) GetProvider() string { @@ -6178,7 +6297,7 @@ type RotateProviderCredentialResponse struct { func (x *RotateProviderCredentialResponse) Reset() { *x = RotateProviderCredentialResponse{} - mi := &file_openshell_proto_msgTypes[84] + mi := &file_openshell_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6190,7 +6309,7 @@ func (x *RotateProviderCredentialResponse) String() string { func (*RotateProviderCredentialResponse) ProtoMessage() {} func (x *RotateProviderCredentialResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[84] + mi := &file_openshell_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6203,7 +6322,7 @@ func (x *RotateProviderCredentialResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RotateProviderCredentialResponse.ProtoReflect.Descriptor instead. func (*RotateProviderCredentialResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{84} + return file_openshell_proto_rawDescGZIP(), []int{86} } func (x *RotateProviderCredentialResponse) GetStatus() *ProviderCredentialRefreshStatus { @@ -6225,7 +6344,7 @@ type DeleteProviderRefreshRequest struct { func (x *DeleteProviderRefreshRequest) Reset() { *x = DeleteProviderRefreshRequest{} - mi := &file_openshell_proto_msgTypes[85] + mi := &file_openshell_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6237,7 +6356,7 @@ func (x *DeleteProviderRefreshRequest) String() string { func (*DeleteProviderRefreshRequest) ProtoMessage() {} func (x *DeleteProviderRefreshRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[85] + mi := &file_openshell_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6250,7 +6369,7 @@ func (x *DeleteProviderRefreshRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteProviderRefreshRequest.ProtoReflect.Descriptor instead. func (*DeleteProviderRefreshRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{85} + return file_openshell_proto_rawDescGZIP(), []int{87} } func (x *DeleteProviderRefreshRequest) GetProvider() string { @@ -6283,7 +6402,7 @@ type DeleteProviderRefreshResponse struct { func (x *DeleteProviderRefreshResponse) Reset() { *x = DeleteProviderRefreshResponse{} - mi := &file_openshell_proto_msgTypes[86] + mi := &file_openshell_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6295,7 +6414,7 @@ func (x *DeleteProviderRefreshResponse) String() string { func (*DeleteProviderRefreshResponse) ProtoMessage() {} func (x *DeleteProviderRefreshResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[86] + mi := &file_openshell_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6308,7 +6427,7 @@ func (x *DeleteProviderRefreshResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteProviderRefreshResponse.ProtoReflect.Descriptor instead. func (*DeleteProviderRefreshResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{86} + return file_openshell_proto_rawDescGZIP(), []int{88} } func (x *DeleteProviderRefreshResponse) GetDeleted() bool { @@ -6348,7 +6467,7 @@ type ProviderProfile struct { func (x *ProviderProfile) Reset() { *x = ProviderProfile{} - mi := &file_openshell_proto_msgTypes[87] + mi := &file_openshell_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6360,7 +6479,7 @@ func (x *ProviderProfile) String() string { func (*ProviderProfile) ProtoMessage() {} func (x *ProviderProfile) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[87] + mi := &file_openshell_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6373,7 +6492,7 @@ func (x *ProviderProfile) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderProfile.ProtoReflect.Descriptor instead. func (*ProviderProfile) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{87} + return file_openshell_proto_rawDescGZIP(), []int{89} } func (x *ProviderProfile) GetId() string { @@ -6478,7 +6597,7 @@ type StoredProviderProfile struct { func (x *StoredProviderProfile) Reset() { *x = StoredProviderProfile{} - mi := &file_openshell_proto_msgTypes[88] + mi := &file_openshell_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6490,7 +6609,7 @@ func (x *StoredProviderProfile) String() string { func (*StoredProviderProfile) ProtoMessage() {} func (x *StoredProviderProfile) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[88] + mi := &file_openshell_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6503,7 +6622,7 @@ func (x *StoredProviderProfile) ProtoReflect() protoreflect.Message { // Deprecated: Use StoredProviderProfile.ProtoReflect.Descriptor instead. func (*StoredProviderProfile) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{88} + return file_openshell_proto_rawDescGZIP(), []int{90} } func (x *StoredProviderProfile) GetMetadata() *datamodelv1.ObjectMeta { @@ -6530,7 +6649,7 @@ type ProviderProfileResponse struct { func (x *ProviderProfileResponse) Reset() { *x = ProviderProfileResponse{} - mi := &file_openshell_proto_msgTypes[89] + mi := &file_openshell_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6542,7 +6661,7 @@ func (x *ProviderProfileResponse) String() string { func (*ProviderProfileResponse) ProtoMessage() {} func (x *ProviderProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[89] + mi := &file_openshell_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6555,7 +6674,7 @@ func (x *ProviderProfileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderProfileResponse.ProtoReflect.Descriptor instead. func (*ProviderProfileResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{89} + return file_openshell_proto_rawDescGZIP(), []int{91} } func (x *ProviderProfileResponse) GetProfile() *ProviderProfile { @@ -6575,7 +6694,7 @@ type ListProviderProfilesResponse struct { func (x *ListProviderProfilesResponse) Reset() { *x = ListProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[90] + mi := &file_openshell_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6587,7 +6706,7 @@ func (x *ListProviderProfilesResponse) String() string { func (*ListProviderProfilesResponse) ProtoMessage() {} func (x *ListProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[90] + mi := &file_openshell_proto_msgTypes[92] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6600,7 +6719,7 @@ func (x *ListProviderProfilesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListProviderProfilesResponse.ProtoReflect.Descriptor instead. func (*ListProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{90} + return file_openshell_proto_rawDescGZIP(), []int{92} } func (x *ListProviderProfilesResponse) GetProfiles() []*ProviderProfile { @@ -6623,7 +6742,7 @@ type ImportProviderProfilesRequest struct { func (x *ImportProviderProfilesRequest) Reset() { *x = ImportProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[91] + mi := &file_openshell_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6635,7 +6754,7 @@ func (x *ImportProviderProfilesRequest) String() string { func (*ImportProviderProfilesRequest) ProtoMessage() {} func (x *ImportProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[91] + mi := &file_openshell_proto_msgTypes[93] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6648,7 +6767,7 @@ func (x *ImportProviderProfilesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ImportProviderProfilesRequest.ProtoReflect.Descriptor instead. func (*ImportProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{91} + return file_openshell_proto_rawDescGZIP(), []int{93} } func (x *ImportProviderProfilesRequest) GetProfiles() []*ProviderProfileImportItem { @@ -6677,7 +6796,7 @@ type ImportProviderProfilesResponse struct { func (x *ImportProviderProfilesResponse) Reset() { *x = ImportProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[92] + mi := &file_openshell_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6689,7 +6808,7 @@ func (x *ImportProviderProfilesResponse) String() string { func (*ImportProviderProfilesResponse) ProtoMessage() {} func (x *ImportProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[92] + mi := &file_openshell_proto_msgTypes[94] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6702,7 +6821,7 @@ func (x *ImportProviderProfilesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ImportProviderProfilesResponse.ProtoReflect.Descriptor instead. func (*ImportProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{92} + return file_openshell_proto_rawDescGZIP(), []int{94} } func (x *ImportProviderProfilesResponse) GetDiagnostics() []*ProviderProfileDiagnostic { @@ -6746,7 +6865,7 @@ type UpdateProviderProfilesRequest struct { func (x *UpdateProviderProfilesRequest) Reset() { *x = UpdateProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[93] + mi := &file_openshell_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6758,7 +6877,7 @@ func (x *UpdateProviderProfilesRequest) String() string { func (*UpdateProviderProfilesRequest) ProtoMessage() {} func (x *UpdateProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[93] + mi := &file_openshell_proto_msgTypes[95] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6771,7 +6890,7 @@ func (x *UpdateProviderProfilesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateProviderProfilesRequest.ProtoReflect.Descriptor instead. func (*UpdateProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{93} + return file_openshell_proto_rawDescGZIP(), []int{95} } func (x *UpdateProviderProfilesRequest) GetProfile() *ProviderProfileImportItem { @@ -6814,7 +6933,7 @@ type UpdateProviderProfilesResponse struct { func (x *UpdateProviderProfilesResponse) Reset() { *x = UpdateProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[94] + mi := &file_openshell_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6826,7 +6945,7 @@ func (x *UpdateProviderProfilesResponse) String() string { func (*UpdateProviderProfilesResponse) ProtoMessage() {} func (x *UpdateProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[94] + mi := &file_openshell_proto_msgTypes[96] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6839,7 +6958,7 @@ func (x *UpdateProviderProfilesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateProviderProfilesResponse.ProtoReflect.Descriptor instead. func (*UpdateProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{94} + return file_openshell_proto_rawDescGZIP(), []int{96} } func (x *UpdateProviderProfilesResponse) GetDiagnostics() []*ProviderProfileDiagnostic { @@ -6876,7 +6995,7 @@ type LintProviderProfilesRequest struct { func (x *LintProviderProfilesRequest) Reset() { *x = LintProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[95] + mi := &file_openshell_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6888,7 +7007,7 @@ func (x *LintProviderProfilesRequest) String() string { func (*LintProviderProfilesRequest) ProtoMessage() {} func (x *LintProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[95] + mi := &file_openshell_proto_msgTypes[97] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6901,7 +7020,7 @@ func (x *LintProviderProfilesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LintProviderProfilesRequest.ProtoReflect.Descriptor instead. func (*LintProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{95} + return file_openshell_proto_rawDescGZIP(), []int{97} } func (x *LintProviderProfilesRequest) GetProfiles() []*ProviderProfileImportItem { @@ -6929,7 +7048,7 @@ type LintProviderProfilesResponse struct { func (x *LintProviderProfilesResponse) Reset() { *x = LintProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[96] + mi := &file_openshell_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6941,7 +7060,7 @@ func (x *LintProviderProfilesResponse) String() string { func (*LintProviderProfilesResponse) ProtoMessage() {} func (x *LintProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[96] + mi := &file_openshell_proto_msgTypes[98] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6954,7 +7073,7 @@ func (x *LintProviderProfilesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LintProviderProfilesResponse.ProtoReflect.Descriptor instead. func (*LintProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{96} + return file_openshell_proto_rawDescGZIP(), []int{98} } func (x *LintProviderProfilesResponse) GetDiagnostics() []*ProviderProfileDiagnostic { @@ -6981,7 +7100,7 @@ type DeleteProviderResponse struct { func (x *DeleteProviderResponse) Reset() { *x = DeleteProviderResponse{} - mi := &file_openshell_proto_msgTypes[97] + mi := &file_openshell_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6993,7 +7112,7 @@ func (x *DeleteProviderResponse) String() string { func (*DeleteProviderResponse) ProtoMessage() {} func (x *DeleteProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[97] + mi := &file_openshell_proto_msgTypes[99] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7006,7 +7125,7 @@ func (x *DeleteProviderResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteProviderResponse.ProtoReflect.Descriptor instead. func (*DeleteProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{97} + return file_openshell_proto_rawDescGZIP(), []int{99} } func (x *DeleteProviderResponse) GetDeleted() bool { @@ -7029,7 +7148,7 @@ type DeleteProviderProfileRequest struct { func (x *DeleteProviderProfileRequest) Reset() { *x = DeleteProviderProfileRequest{} - mi := &file_openshell_proto_msgTypes[98] + mi := &file_openshell_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7041,7 +7160,7 @@ func (x *DeleteProviderProfileRequest) String() string { func (*DeleteProviderProfileRequest) ProtoMessage() {} func (x *DeleteProviderProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[98] + mi := &file_openshell_proto_msgTypes[100] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7054,7 +7173,7 @@ func (x *DeleteProviderProfileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteProviderProfileRequest.ProtoReflect.Descriptor instead. func (*DeleteProviderProfileRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{98} + return file_openshell_proto_rawDescGZIP(), []int{100} } func (x *DeleteProviderProfileRequest) GetId() string { @@ -7081,7 +7200,7 @@ type DeleteProviderProfileResponse struct { func (x *DeleteProviderProfileResponse) Reset() { *x = DeleteProviderProfileResponse{} - mi := &file_openshell_proto_msgTypes[99] + mi := &file_openshell_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7093,7 +7212,7 @@ func (x *DeleteProviderProfileResponse) String() string { func (*DeleteProviderProfileResponse) ProtoMessage() {} func (x *DeleteProviderProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[99] + mi := &file_openshell_proto_msgTypes[101] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7106,7 +7225,7 @@ func (x *DeleteProviderProfileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteProviderProfileResponse.ProtoReflect.Descriptor instead. func (*DeleteProviderProfileResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{99} + return file_openshell_proto_rawDescGZIP(), []int{101} } func (x *DeleteProviderProfileResponse) GetDeleted() bool { @@ -7131,7 +7250,7 @@ type GetSandboxProviderEnvironmentRequest struct { func (x *GetSandboxProviderEnvironmentRequest) Reset() { *x = GetSandboxProviderEnvironmentRequest{} - mi := &file_openshell_proto_msgTypes[100] + mi := &file_openshell_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7143,7 +7262,7 @@ func (x *GetSandboxProviderEnvironmentRequest) String() string { func (*GetSandboxProviderEnvironmentRequest) ProtoMessage() {} func (x *GetSandboxProviderEnvironmentRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[100] + mi := &file_openshell_proto_msgTypes[102] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7156,7 +7275,7 @@ func (x *GetSandboxProviderEnvironmentRequest) ProtoReflect() protoreflect.Messa // Deprecated: Use GetSandboxProviderEnvironmentRequest.ProtoReflect.Descriptor instead. func (*GetSandboxProviderEnvironmentRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{100} + return file_openshell_proto_rawDescGZIP(), []int{102} } func (x *GetSandboxProviderEnvironmentRequest) GetSandboxId() string { @@ -7185,7 +7304,7 @@ type StaticCredentialEndpointBinding struct { func (x *StaticCredentialEndpointBinding) Reset() { *x = StaticCredentialEndpointBinding{} - mi := &file_openshell_proto_msgTypes[101] + mi := &file_openshell_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7197,7 +7316,7 @@ func (x *StaticCredentialEndpointBinding) String() string { func (*StaticCredentialEndpointBinding) ProtoMessage() {} func (x *StaticCredentialEndpointBinding) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[101] + mi := &file_openshell_proto_msgTypes[103] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7210,7 +7329,7 @@ func (x *StaticCredentialEndpointBinding) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticCredentialEndpointBinding.ProtoReflect.Descriptor instead. func (*StaticCredentialEndpointBinding) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{101} + return file_openshell_proto_rawDescGZIP(), []int{103} } func (x *StaticCredentialEndpointBinding) GetHost() string { @@ -7248,7 +7367,7 @@ type StaticCredentialBinding struct { func (x *StaticCredentialBinding) Reset() { *x = StaticCredentialBinding{} - mi := &file_openshell_proto_msgTypes[102] + mi := &file_openshell_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7260,7 +7379,7 @@ func (x *StaticCredentialBinding) String() string { func (*StaticCredentialBinding) ProtoMessage() {} func (x *StaticCredentialBinding) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[102] + mi := &file_openshell_proto_msgTypes[104] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7273,7 +7392,7 @@ func (x *StaticCredentialBinding) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticCredentialBinding.ProtoReflect.Descriptor instead. func (*StaticCredentialBinding) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{102} + return file_openshell_proto_rawDescGZIP(), []int{104} } func (x *StaticCredentialBinding) GetEndpoints() []*StaticCredentialEndpointBinding { @@ -7317,7 +7436,7 @@ type GetSandboxProviderEnvironmentResponse struct { func (x *GetSandboxProviderEnvironmentResponse) Reset() { *x = GetSandboxProviderEnvironmentResponse{} - mi := &file_openshell_proto_msgTypes[103] + mi := &file_openshell_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7329,7 +7448,7 @@ func (x *GetSandboxProviderEnvironmentResponse) String() string { func (*GetSandboxProviderEnvironmentResponse) ProtoMessage() {} func (x *GetSandboxProviderEnvironmentResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[103] + mi := &file_openshell_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7342,7 +7461,7 @@ func (x *GetSandboxProviderEnvironmentResponse) ProtoReflect() protoreflect.Mess // Deprecated: Use GetSandboxProviderEnvironmentResponse.ProtoReflect.Descriptor instead. func (*GetSandboxProviderEnvironmentResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{103} + return file_openshell_proto_rawDescGZIP(), []int{105} } func (x *GetSandboxProviderEnvironmentResponse) GetEnvironment() map[string]string { @@ -7434,7 +7553,7 @@ type UpdateConfigRequest struct { func (x *UpdateConfigRequest) Reset() { *x = UpdateConfigRequest{} - mi := &file_openshell_proto_msgTypes[104] + mi := &file_openshell_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7446,7 +7565,7 @@ func (x *UpdateConfigRequest) String() string { func (*UpdateConfigRequest) ProtoMessage() {} func (x *UpdateConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[104] + mi := &file_openshell_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7459,7 +7578,7 @@ func (x *UpdateConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateConfigRequest.ProtoReflect.Descriptor instead. func (*UpdateConfigRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{104} + return file_openshell_proto_rawDescGZIP(), []int{106} } func (x *UpdateConfigRequest) GetName() string { @@ -7549,7 +7668,7 @@ type PolicyMergeOperation struct { func (x *PolicyMergeOperation) Reset() { *x = PolicyMergeOperation{} - mi := &file_openshell_proto_msgTypes[105] + mi := &file_openshell_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7561,7 +7680,7 @@ func (x *PolicyMergeOperation) String() string { func (*PolicyMergeOperation) ProtoMessage() {} func (x *PolicyMergeOperation) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[105] + mi := &file_openshell_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7574,7 +7693,7 @@ func (x *PolicyMergeOperation) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyMergeOperation.ProtoReflect.Descriptor instead. func (*PolicyMergeOperation) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{105} + return file_openshell_proto_rawDescGZIP(), []int{107} } func (x *PolicyMergeOperation) GetOperation() isPolicyMergeOperation_Operation { @@ -7688,7 +7807,7 @@ type AddNetworkRule struct { func (x *AddNetworkRule) Reset() { *x = AddNetworkRule{} - mi := &file_openshell_proto_msgTypes[106] + mi := &file_openshell_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7700,7 +7819,7 @@ func (x *AddNetworkRule) String() string { func (*AddNetworkRule) ProtoMessage() {} func (x *AddNetworkRule) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[106] + mi := &file_openshell_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7713,7 +7832,7 @@ func (x *AddNetworkRule) ProtoReflect() protoreflect.Message { // Deprecated: Use AddNetworkRule.ProtoReflect.Descriptor instead. func (*AddNetworkRule) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{106} + return file_openshell_proto_rawDescGZIP(), []int{108} } func (x *AddNetworkRule) GetRuleName() string { @@ -7741,7 +7860,7 @@ type RemoveNetworkEndpoint struct { func (x *RemoveNetworkEndpoint) Reset() { *x = RemoveNetworkEndpoint{} - mi := &file_openshell_proto_msgTypes[107] + mi := &file_openshell_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7753,7 +7872,7 @@ func (x *RemoveNetworkEndpoint) String() string { func (*RemoveNetworkEndpoint) ProtoMessage() {} func (x *RemoveNetworkEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[107] + mi := &file_openshell_proto_msgTypes[109] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7766,7 +7885,7 @@ func (x *RemoveNetworkEndpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveNetworkEndpoint.ProtoReflect.Descriptor instead. func (*RemoveNetworkEndpoint) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{107} + return file_openshell_proto_rawDescGZIP(), []int{109} } func (x *RemoveNetworkEndpoint) GetRuleName() string { @@ -7799,7 +7918,7 @@ type RemoveNetworkRule struct { func (x *RemoveNetworkRule) Reset() { *x = RemoveNetworkRule{} - mi := &file_openshell_proto_msgTypes[108] + mi := &file_openshell_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7811,7 +7930,7 @@ func (x *RemoveNetworkRule) String() string { func (*RemoveNetworkRule) ProtoMessage() {} func (x *RemoveNetworkRule) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[108] + mi := &file_openshell_proto_msgTypes[110] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7824,7 +7943,7 @@ func (x *RemoveNetworkRule) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveNetworkRule.ProtoReflect.Descriptor instead. func (*RemoveNetworkRule) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{108} + return file_openshell_proto_rawDescGZIP(), []int{110} } func (x *RemoveNetworkRule) GetRuleName() string { @@ -7845,7 +7964,7 @@ type AddDenyRules struct { func (x *AddDenyRules) Reset() { *x = AddDenyRules{} - mi := &file_openshell_proto_msgTypes[109] + mi := &file_openshell_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7857,7 +7976,7 @@ func (x *AddDenyRules) String() string { func (*AddDenyRules) ProtoMessage() {} func (x *AddDenyRules) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[109] + mi := &file_openshell_proto_msgTypes[111] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7870,7 +7989,7 @@ func (x *AddDenyRules) ProtoReflect() protoreflect.Message { // Deprecated: Use AddDenyRules.ProtoReflect.Descriptor instead. func (*AddDenyRules) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{109} + return file_openshell_proto_rawDescGZIP(), []int{111} } func (x *AddDenyRules) GetHost() string { @@ -7905,7 +8024,7 @@ type AddAllowRules struct { func (x *AddAllowRules) Reset() { *x = AddAllowRules{} - mi := &file_openshell_proto_msgTypes[110] + mi := &file_openshell_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7917,7 +8036,7 @@ func (x *AddAllowRules) String() string { func (*AddAllowRules) ProtoMessage() {} func (x *AddAllowRules) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[110] + mi := &file_openshell_proto_msgTypes[112] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7930,7 +8049,7 @@ func (x *AddAllowRules) ProtoReflect() protoreflect.Message { // Deprecated: Use AddAllowRules.ProtoReflect.Descriptor instead. func (*AddAllowRules) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{110} + return file_openshell_proto_rawDescGZIP(), []int{112} } func (x *AddAllowRules) GetHost() string { @@ -7964,7 +8083,7 @@ type RemoveNetworkBinary struct { func (x *RemoveNetworkBinary) Reset() { *x = RemoveNetworkBinary{} - mi := &file_openshell_proto_msgTypes[111] + mi := &file_openshell_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7976,7 +8095,7 @@ func (x *RemoveNetworkBinary) String() string { func (*RemoveNetworkBinary) ProtoMessage() {} func (x *RemoveNetworkBinary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[111] + mi := &file_openshell_proto_msgTypes[113] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7989,7 +8108,7 @@ func (x *RemoveNetworkBinary) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveNetworkBinary.ProtoReflect.Descriptor instead. func (*RemoveNetworkBinary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{111} + return file_openshell_proto_rawDescGZIP(), []int{113} } func (x *RemoveNetworkBinary) GetRuleName() string { @@ -8025,7 +8144,7 @@ type UpdateConfigResponse struct { func (x *UpdateConfigResponse) Reset() { *x = UpdateConfigResponse{} - mi := &file_openshell_proto_msgTypes[112] + mi := &file_openshell_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8037,7 +8156,7 @@ func (x *UpdateConfigResponse) String() string { func (*UpdateConfigResponse) ProtoMessage() {} func (x *UpdateConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[112] + mi := &file_openshell_proto_msgTypes[114] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8050,7 +8169,7 @@ func (x *UpdateConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateConfigResponse.ProtoReflect.Descriptor instead. func (*UpdateConfigResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{112} + return file_openshell_proto_rawDescGZIP(), []int{114} } func (x *UpdateConfigResponse) GetVersion() uint32 { @@ -8105,7 +8224,7 @@ type GetSandboxPolicyStatusRequest struct { func (x *GetSandboxPolicyStatusRequest) Reset() { *x = GetSandboxPolicyStatusRequest{} - mi := &file_openshell_proto_msgTypes[113] + mi := &file_openshell_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8117,7 +8236,7 @@ func (x *GetSandboxPolicyStatusRequest) String() string { func (*GetSandboxPolicyStatusRequest) ProtoMessage() {} func (x *GetSandboxPolicyStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[113] + mi := &file_openshell_proto_msgTypes[115] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8130,7 +8249,7 @@ func (x *GetSandboxPolicyStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSandboxPolicyStatusRequest.ProtoReflect.Descriptor instead. func (*GetSandboxPolicyStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{113} + return file_openshell_proto_rawDescGZIP(), []int{115} } func (x *GetSandboxPolicyStatusRequest) GetName() string { @@ -8174,7 +8293,7 @@ type GetSandboxPolicyStatusResponse struct { func (x *GetSandboxPolicyStatusResponse) Reset() { *x = GetSandboxPolicyStatusResponse{} - mi := &file_openshell_proto_msgTypes[114] + mi := &file_openshell_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8186,7 +8305,7 @@ func (x *GetSandboxPolicyStatusResponse) String() string { func (*GetSandboxPolicyStatusResponse) ProtoMessage() {} func (x *GetSandboxPolicyStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[114] + mi := &file_openshell_proto_msgTypes[116] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8199,7 +8318,7 @@ func (x *GetSandboxPolicyStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSandboxPolicyStatusResponse.ProtoReflect.Descriptor instead. func (*GetSandboxPolicyStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{114} + return file_openshell_proto_rawDescGZIP(), []int{116} } func (x *GetSandboxPolicyStatusResponse) GetRevision() *SandboxPolicyRevision { @@ -8233,7 +8352,7 @@ type ListSandboxPoliciesRequest struct { func (x *ListSandboxPoliciesRequest) Reset() { *x = ListSandboxPoliciesRequest{} - mi := &file_openshell_proto_msgTypes[115] + mi := &file_openshell_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8245,7 +8364,7 @@ func (x *ListSandboxPoliciesRequest) String() string { func (*ListSandboxPoliciesRequest) ProtoMessage() {} func (x *ListSandboxPoliciesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[115] + mi := &file_openshell_proto_msgTypes[117] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8258,7 +8377,7 @@ func (x *ListSandboxPoliciesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSandboxPoliciesRequest.ProtoReflect.Descriptor instead. func (*ListSandboxPoliciesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{115} + return file_openshell_proto_rawDescGZIP(), []int{117} } func (x *ListSandboxPoliciesRequest) GetName() string { @@ -8306,7 +8425,7 @@ type ListSandboxPoliciesResponse struct { func (x *ListSandboxPoliciesResponse) Reset() { *x = ListSandboxPoliciesResponse{} - mi := &file_openshell_proto_msgTypes[116] + mi := &file_openshell_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8318,7 +8437,7 @@ func (x *ListSandboxPoliciesResponse) String() string { func (*ListSandboxPoliciesResponse) ProtoMessage() {} func (x *ListSandboxPoliciesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[116] + mi := &file_openshell_proto_msgTypes[118] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8331,7 +8450,7 @@ func (x *ListSandboxPoliciesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSandboxPoliciesResponse.ProtoReflect.Descriptor instead. func (*ListSandboxPoliciesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{116} + return file_openshell_proto_rawDescGZIP(), []int{118} } func (x *ListSandboxPoliciesResponse) GetRevisions() []*SandboxPolicyRevision { @@ -8358,7 +8477,7 @@ type ReportPolicyStatusRequest struct { func (x *ReportPolicyStatusRequest) Reset() { *x = ReportPolicyStatusRequest{} - mi := &file_openshell_proto_msgTypes[117] + mi := &file_openshell_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8370,7 +8489,7 @@ func (x *ReportPolicyStatusRequest) String() string { func (*ReportPolicyStatusRequest) ProtoMessage() {} func (x *ReportPolicyStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[117] + mi := &file_openshell_proto_msgTypes[119] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8383,7 +8502,7 @@ func (x *ReportPolicyStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportPolicyStatusRequest.ProtoReflect.Descriptor instead. func (*ReportPolicyStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{117} + return file_openshell_proto_rawDescGZIP(), []int{119} } func (x *ReportPolicyStatusRequest) GetSandboxId() string { @@ -8423,7 +8542,7 @@ type ReportPolicyStatusResponse struct { func (x *ReportPolicyStatusResponse) Reset() { *x = ReportPolicyStatusResponse{} - mi := &file_openshell_proto_msgTypes[118] + mi := &file_openshell_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8435,7 +8554,7 @@ func (x *ReportPolicyStatusResponse) String() string { func (*ReportPolicyStatusResponse) ProtoMessage() {} func (x *ReportPolicyStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[118] + mi := &file_openshell_proto_msgTypes[120] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8448,7 +8567,7 @@ func (x *ReportPolicyStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportPolicyStatusResponse.ProtoReflect.Descriptor instead. func (*ReportPolicyStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{118} + return file_openshell_proto_rawDescGZIP(), []int{120} } // A versioned policy revision with metadata. @@ -8476,7 +8595,7 @@ type SandboxPolicyRevision struct { func (x *SandboxPolicyRevision) Reset() { *x = SandboxPolicyRevision{} - mi := &file_openshell_proto_msgTypes[119] + mi := &file_openshell_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8488,7 +8607,7 @@ func (x *SandboxPolicyRevision) String() string { func (*SandboxPolicyRevision) ProtoMessage() {} func (x *SandboxPolicyRevision) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[119] + mi := &file_openshell_proto_msgTypes[121] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8501,7 +8620,7 @@ func (x *SandboxPolicyRevision) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxPolicyRevision.ProtoReflect.Descriptor instead. func (*SandboxPolicyRevision) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{119} + return file_openshell_proto_rawDescGZIP(), []int{121} } func (x *SandboxPolicyRevision) GetVersion() uint32 { @@ -8581,7 +8700,7 @@ type GetSandboxLogsRequest struct { func (x *GetSandboxLogsRequest) Reset() { *x = GetSandboxLogsRequest{} - mi := &file_openshell_proto_msgTypes[120] + mi := &file_openshell_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8593,7 +8712,7 @@ func (x *GetSandboxLogsRequest) String() string { func (*GetSandboxLogsRequest) ProtoMessage() {} func (x *GetSandboxLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[120] + mi := &file_openshell_proto_msgTypes[122] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8606,7 +8725,7 @@ func (x *GetSandboxLogsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSandboxLogsRequest.ProtoReflect.Descriptor instead. func (*GetSandboxLogsRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{120} + return file_openshell_proto_rawDescGZIP(), []int{122} } func (x *GetSandboxLogsRequest) GetSandboxId() string { @@ -8664,7 +8783,7 @@ type PushSandboxLogsRequest struct { func (x *PushSandboxLogsRequest) Reset() { *x = PushSandboxLogsRequest{} - mi := &file_openshell_proto_msgTypes[121] + mi := &file_openshell_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8676,7 +8795,7 @@ func (x *PushSandboxLogsRequest) String() string { func (*PushSandboxLogsRequest) ProtoMessage() {} func (x *PushSandboxLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[121] + mi := &file_openshell_proto_msgTypes[123] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8689,7 +8808,7 @@ func (x *PushSandboxLogsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PushSandboxLogsRequest.ProtoReflect.Descriptor instead. func (*PushSandboxLogsRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{121} + return file_openshell_proto_rawDescGZIP(), []int{123} } func (x *PushSandboxLogsRequest) GetSandboxId() string { @@ -8715,7 +8834,7 @@ type PushSandboxLogsResponse struct { func (x *PushSandboxLogsResponse) Reset() { *x = PushSandboxLogsResponse{} - mi := &file_openshell_proto_msgTypes[122] + mi := &file_openshell_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8727,7 +8846,7 @@ func (x *PushSandboxLogsResponse) String() string { func (*PushSandboxLogsResponse) ProtoMessage() {} func (x *PushSandboxLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[122] + mi := &file_openshell_proto_msgTypes[124] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8740,7 +8859,7 @@ func (x *PushSandboxLogsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PushSandboxLogsResponse.ProtoReflect.Descriptor instead. func (*PushSandboxLogsResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{122} + return file_openshell_proto_rawDescGZIP(), []int{124} } // Get sandbox logs response. @@ -8756,7 +8875,7 @@ type GetSandboxLogsResponse struct { func (x *GetSandboxLogsResponse) Reset() { *x = GetSandboxLogsResponse{} - mi := &file_openshell_proto_msgTypes[123] + mi := &file_openshell_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8768,7 +8887,7 @@ func (x *GetSandboxLogsResponse) String() string { func (*GetSandboxLogsResponse) ProtoMessage() {} func (x *GetSandboxLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[123] + mi := &file_openshell_proto_msgTypes[125] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8781,7 +8900,7 @@ func (x *GetSandboxLogsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSandboxLogsResponse.ProtoReflect.Descriptor instead. func (*GetSandboxLogsResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{123} + return file_openshell_proto_rawDescGZIP(), []int{125} } func (x *GetSandboxLogsResponse) GetLogs() []*SandboxLogLine { @@ -8814,7 +8933,7 @@ type SupervisorMessage struct { func (x *SupervisorMessage) Reset() { *x = SupervisorMessage{} - mi := &file_openshell_proto_msgTypes[124] + mi := &file_openshell_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8826,7 +8945,7 @@ func (x *SupervisorMessage) String() string { func (*SupervisorMessage) ProtoMessage() {} func (x *SupervisorMessage) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[124] + mi := &file_openshell_proto_msgTypes[126] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8839,7 +8958,7 @@ func (x *SupervisorMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use SupervisorMessage.ProtoReflect.Descriptor instead. func (*SupervisorMessage) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{124} + return file_openshell_proto_rawDescGZIP(), []int{126} } func (x *SupervisorMessage) GetPayload() isSupervisorMessage_Payload { @@ -8930,7 +9049,7 @@ type GatewayMessage struct { func (x *GatewayMessage) Reset() { *x = GatewayMessage{} - mi := &file_openshell_proto_msgTypes[125] + mi := &file_openshell_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8942,7 +9061,7 @@ func (x *GatewayMessage) String() string { func (*GatewayMessage) ProtoMessage() {} func (x *GatewayMessage) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[125] + mi := &file_openshell_proto_msgTypes[127] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8955,7 +9074,7 @@ func (x *GatewayMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewayMessage.ProtoReflect.Descriptor instead. func (*GatewayMessage) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{125} + return file_openshell_proto_rawDescGZIP(), []int{127} } func (x *GatewayMessage) GetPayload() isGatewayMessage_Payload { @@ -9057,7 +9176,7 @@ type SupervisorHello struct { func (x *SupervisorHello) Reset() { *x = SupervisorHello{} - mi := &file_openshell_proto_msgTypes[126] + mi := &file_openshell_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9069,7 +9188,7 @@ func (x *SupervisorHello) String() string { func (*SupervisorHello) ProtoMessage() {} func (x *SupervisorHello) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[126] + mi := &file_openshell_proto_msgTypes[128] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9082,7 +9201,7 @@ func (x *SupervisorHello) ProtoReflect() protoreflect.Message { // Deprecated: Use SupervisorHello.ProtoReflect.Descriptor instead. func (*SupervisorHello) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{126} + return file_openshell_proto_rawDescGZIP(), []int{128} } func (x *SupervisorHello) GetSandboxId() string { @@ -9112,7 +9231,7 @@ type SessionAccepted struct { func (x *SessionAccepted) Reset() { *x = SessionAccepted{} - mi := &file_openshell_proto_msgTypes[127] + mi := &file_openshell_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9124,7 +9243,7 @@ func (x *SessionAccepted) String() string { func (*SessionAccepted) ProtoMessage() {} func (x *SessionAccepted) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[127] + mi := &file_openshell_proto_msgTypes[129] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9137,7 +9256,7 @@ func (x *SessionAccepted) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionAccepted.ProtoReflect.Descriptor instead. func (*SessionAccepted) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{127} + return file_openshell_proto_rawDescGZIP(), []int{129} } func (x *SessionAccepted) GetSessionId() string { @@ -9165,7 +9284,7 @@ type SessionRejected struct { func (x *SessionRejected) Reset() { *x = SessionRejected{} - mi := &file_openshell_proto_msgTypes[128] + mi := &file_openshell_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9177,7 +9296,7 @@ func (x *SessionRejected) String() string { func (*SessionRejected) ProtoMessage() {} func (x *SessionRejected) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[128] + mi := &file_openshell_proto_msgTypes[130] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9190,7 +9309,7 @@ func (x *SessionRejected) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionRejected.ProtoReflect.Descriptor instead. func (*SessionRejected) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{128} + return file_openshell_proto_rawDescGZIP(), []int{130} } func (x *SessionRejected) GetReason() string { @@ -9209,7 +9328,7 @@ type SupervisorHeartbeat struct { func (x *SupervisorHeartbeat) Reset() { *x = SupervisorHeartbeat{} - mi := &file_openshell_proto_msgTypes[129] + mi := &file_openshell_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9221,7 +9340,7 @@ func (x *SupervisorHeartbeat) String() string { func (*SupervisorHeartbeat) ProtoMessage() {} func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[129] + mi := &file_openshell_proto_msgTypes[131] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9234,7 +9353,7 @@ func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { // Deprecated: Use SupervisorHeartbeat.ProtoReflect.Descriptor instead. func (*SupervisorHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{129} + return file_openshell_proto_rawDescGZIP(), []int{131} } // Gateway heartbeat. @@ -9246,7 +9365,7 @@ type GatewayHeartbeat struct { func (x *GatewayHeartbeat) Reset() { *x = GatewayHeartbeat{} - mi := &file_openshell_proto_msgTypes[130] + mi := &file_openshell_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9258,7 +9377,7 @@ func (x *GatewayHeartbeat) String() string { func (*GatewayHeartbeat) ProtoMessage() {} func (x *GatewayHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[130] + mi := &file_openshell_proto_msgTypes[132] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9271,7 +9390,7 @@ func (x *GatewayHeartbeat) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewayHeartbeat.ProtoReflect.Descriptor instead. func (*GatewayHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{130} + return file_openshell_proto_rawDescGZIP(), []int{132} } // Gateway requests the supervisor to open a relay channel. @@ -9300,7 +9419,7 @@ type RelayOpen struct { func (x *RelayOpen) Reset() { *x = RelayOpen{} - mi := &file_openshell_proto_msgTypes[131] + mi := &file_openshell_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9312,7 +9431,7 @@ func (x *RelayOpen) String() string { func (*RelayOpen) ProtoMessage() {} func (x *RelayOpen) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[131] + mi := &file_openshell_proto_msgTypes[133] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9325,7 +9444,7 @@ func (x *RelayOpen) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayOpen.ProtoReflect.Descriptor instead. func (*RelayOpen) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{131} + return file_openshell_proto_rawDescGZIP(), []int{133} } func (x *RelayOpen) GetChannelId() string { @@ -9392,7 +9511,7 @@ type SshRelayTarget struct { func (x *SshRelayTarget) Reset() { *x = SshRelayTarget{} - mi := &file_openshell_proto_msgTypes[132] + mi := &file_openshell_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9404,7 +9523,7 @@ func (x *SshRelayTarget) String() string { func (*SshRelayTarget) ProtoMessage() {} func (x *SshRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[132] + mi := &file_openshell_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9417,7 +9536,7 @@ func (x *SshRelayTarget) ProtoReflect() protoreflect.Message { // Deprecated: Use SshRelayTarget.ProtoReflect.Descriptor instead. func (*SshRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{132} + return file_openshell_proto_rawDescGZIP(), []int{134} } // TCP target dialed by the supervisor from inside the sandbox. @@ -9433,7 +9552,7 @@ type TcpRelayTarget struct { func (x *TcpRelayTarget) Reset() { *x = TcpRelayTarget{} - mi := &file_openshell_proto_msgTypes[133] + mi := &file_openshell_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9445,7 +9564,7 @@ func (x *TcpRelayTarget) String() string { func (*TcpRelayTarget) ProtoMessage() {} func (x *TcpRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[133] + mi := &file_openshell_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9458,7 +9577,7 @@ func (x *TcpRelayTarget) ProtoReflect() protoreflect.Message { // Deprecated: Use TcpRelayTarget.ProtoReflect.Descriptor instead. func (*TcpRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{133} + return file_openshell_proto_rawDescGZIP(), []int{135} } func (x *TcpRelayTarget) GetHost() string { @@ -9486,7 +9605,7 @@ type RelayInit struct { func (x *RelayInit) Reset() { *x = RelayInit{} - mi := &file_openshell_proto_msgTypes[134] + mi := &file_openshell_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9498,7 +9617,7 @@ func (x *RelayInit) String() string { func (*RelayInit) ProtoMessage() {} func (x *RelayInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[134] + mi := &file_openshell_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9511,7 +9630,7 @@ func (x *RelayInit) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayInit.ProtoReflect.Descriptor instead. func (*RelayInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{134} + return file_openshell_proto_rawDescGZIP(), []int{136} } func (x *RelayInit) GetChannelId() string { @@ -9538,7 +9657,7 @@ type RelayFrame struct { func (x *RelayFrame) Reset() { *x = RelayFrame{} - mi := &file_openshell_proto_msgTypes[135] + mi := &file_openshell_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9550,7 +9669,7 @@ func (x *RelayFrame) String() string { func (*RelayFrame) ProtoMessage() {} func (x *RelayFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[135] + mi := &file_openshell_proto_msgTypes[137] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9563,7 +9682,7 @@ func (x *RelayFrame) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayFrame.ProtoReflect.Descriptor instead. func (*RelayFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{135} + return file_openshell_proto_rawDescGZIP(), []int{137} } func (x *RelayFrame) GetPayload() isRelayFrame_Payload { @@ -9622,7 +9741,7 @@ type RelayOpenResult struct { func (x *RelayOpenResult) Reset() { *x = RelayOpenResult{} - mi := &file_openshell_proto_msgTypes[136] + mi := &file_openshell_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9634,7 +9753,7 @@ func (x *RelayOpenResult) String() string { func (*RelayOpenResult) ProtoMessage() {} func (x *RelayOpenResult) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[136] + mi := &file_openshell_proto_msgTypes[138] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9647,7 +9766,7 @@ func (x *RelayOpenResult) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayOpenResult.ProtoReflect.Descriptor instead. func (*RelayOpenResult) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{136} + return file_openshell_proto_rawDescGZIP(), []int{138} } func (x *RelayOpenResult) GetChannelId() string { @@ -9684,7 +9803,7 @@ type RelayClose struct { func (x *RelayClose) Reset() { *x = RelayClose{} - mi := &file_openshell_proto_msgTypes[137] + mi := &file_openshell_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9696,7 +9815,7 @@ func (x *RelayClose) String() string { func (*RelayClose) ProtoMessage() {} func (x *RelayClose) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[137] + mi := &file_openshell_proto_msgTypes[139] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9709,7 +9828,7 @@ func (x *RelayClose) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayClose.ProtoReflect.Descriptor instead. func (*RelayClose) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{137} + return file_openshell_proto_rawDescGZIP(), []int{139} } func (x *RelayClose) GetChannelId() string { @@ -9743,7 +9862,7 @@ type L7RequestSample struct { func (x *L7RequestSample) Reset() { *x = L7RequestSample{} - mi := &file_openshell_proto_msgTypes[138] + mi := &file_openshell_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9755,7 +9874,7 @@ func (x *L7RequestSample) String() string { func (*L7RequestSample) ProtoMessage() {} func (x *L7RequestSample) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[138] + mi := &file_openshell_proto_msgTypes[140] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9768,7 +9887,7 @@ func (x *L7RequestSample) ProtoReflect() protoreflect.Message { // Deprecated: Use L7RequestSample.ProtoReflect.Descriptor instead. func (*L7RequestSample) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{138} + return file_openshell_proto_rawDescGZIP(), []int{140} } func (x *L7RequestSample) GetMethod() string { @@ -9842,7 +9961,7 @@ type DenialSummary struct { func (x *DenialSummary) Reset() { *x = DenialSummary{} - mi := &file_openshell_proto_msgTypes[139] + mi := &file_openshell_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9854,7 +9973,7 @@ func (x *DenialSummary) String() string { func (*DenialSummary) ProtoMessage() {} func (x *DenialSummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[139] + mi := &file_openshell_proto_msgTypes[141] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9867,7 +9986,7 @@ func (x *DenialSummary) ProtoReflect() protoreflect.Message { // Deprecated: Use DenialSummary.ProtoReflect.Descriptor instead. func (*DenialSummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{139} + return file_openshell_proto_rawDescGZIP(), []int{141} } func (x *DenialSummary) GetSandboxId() string { @@ -10002,7 +10121,7 @@ type DenialGroupCount struct { func (x *DenialGroupCount) Reset() { *x = DenialGroupCount{} - mi := &file_openshell_proto_msgTypes[140] + mi := &file_openshell_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10014,7 +10133,7 @@ func (x *DenialGroupCount) String() string { func (*DenialGroupCount) ProtoMessage() {} func (x *DenialGroupCount) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[140] + mi := &file_openshell_proto_msgTypes[142] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10027,7 +10146,7 @@ func (x *DenialGroupCount) ProtoReflect() protoreflect.Message { // Deprecated: Use DenialGroupCount.ProtoReflect.Descriptor instead. func (*DenialGroupCount) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{140} + return file_openshell_proto_rawDescGZIP(), []int{142} } func (x *DenialGroupCount) GetDenyGroup() string { @@ -10060,7 +10179,7 @@ type NetworkActivitySummary struct { func (x *NetworkActivitySummary) Reset() { *x = NetworkActivitySummary{} - mi := &file_openshell_proto_msgTypes[141] + mi := &file_openshell_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10072,7 +10191,7 @@ func (x *NetworkActivitySummary) String() string { func (*NetworkActivitySummary) ProtoMessage() {} func (x *NetworkActivitySummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[141] + mi := &file_openshell_proto_msgTypes[143] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10085,7 +10204,7 @@ func (x *NetworkActivitySummary) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkActivitySummary.ProtoReflect.Descriptor instead. func (*NetworkActivitySummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{141} + return file_openshell_proto_rawDescGZIP(), []int{143} } func (x *NetworkActivitySummary) GetNetworkActivityCount() uint32 { @@ -10159,7 +10278,7 @@ type PolicyChunk struct { func (x *PolicyChunk) Reset() { *x = PolicyChunk{} - mi := &file_openshell_proto_msgTypes[142] + mi := &file_openshell_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10171,7 +10290,7 @@ func (x *PolicyChunk) String() string { func (*PolicyChunk) ProtoMessage() {} func (x *PolicyChunk) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[142] + mi := &file_openshell_proto_msgTypes[144] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10184,7 +10303,7 @@ func (x *PolicyChunk) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyChunk.ProtoReflect.Descriptor instead. func (*PolicyChunk) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{142} + return file_openshell_proto_rawDescGZIP(), []int{144} } func (x *PolicyChunk) GetId() string { @@ -10330,7 +10449,7 @@ type DraftPolicyUpdate struct { func (x *DraftPolicyUpdate) Reset() { *x = DraftPolicyUpdate{} - mi := &file_openshell_proto_msgTypes[143] + mi := &file_openshell_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10342,7 +10461,7 @@ func (x *DraftPolicyUpdate) String() string { func (*DraftPolicyUpdate) ProtoMessage() {} func (x *DraftPolicyUpdate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[143] + mi := &file_openshell_proto_msgTypes[145] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10355,7 +10474,7 @@ func (x *DraftPolicyUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftPolicyUpdate.ProtoReflect.Descriptor instead. func (*DraftPolicyUpdate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{143} + return file_openshell_proto_rawDescGZIP(), []int{145} } func (x *DraftPolicyUpdate) GetDraftVersion() uint64 { @@ -10413,7 +10532,7 @@ type SubmitPolicyAnalysisRequest struct { func (x *SubmitPolicyAnalysisRequest) Reset() { *x = SubmitPolicyAnalysisRequest{} - mi := &file_openshell_proto_msgTypes[144] + mi := &file_openshell_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10425,7 +10544,7 @@ func (x *SubmitPolicyAnalysisRequest) String() string { func (*SubmitPolicyAnalysisRequest) ProtoMessage() {} func (x *SubmitPolicyAnalysisRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[144] + mi := &file_openshell_proto_msgTypes[146] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10438,7 +10557,7 @@ func (x *SubmitPolicyAnalysisRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitPolicyAnalysisRequest.ProtoReflect.Descriptor instead. func (*SubmitPolicyAnalysisRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{144} + return file_openshell_proto_rawDescGZIP(), []int{146} } func (x *SubmitPolicyAnalysisRequest) GetSummaries() []*DenialSummary { @@ -10501,7 +10620,7 @@ type SubmitPolicyAnalysisResponse struct { func (x *SubmitPolicyAnalysisResponse) Reset() { *x = SubmitPolicyAnalysisResponse{} - mi := &file_openshell_proto_msgTypes[145] + mi := &file_openshell_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10513,7 +10632,7 @@ func (x *SubmitPolicyAnalysisResponse) String() string { func (*SubmitPolicyAnalysisResponse) ProtoMessage() {} func (x *SubmitPolicyAnalysisResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[145] + mi := &file_openshell_proto_msgTypes[147] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10526,7 +10645,7 @@ func (x *SubmitPolicyAnalysisResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitPolicyAnalysisResponse.ProtoReflect.Descriptor instead. func (*SubmitPolicyAnalysisResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{145} + return file_openshell_proto_rawDescGZIP(), []int{147} } func (x *SubmitPolicyAnalysisResponse) GetAcceptedChunks() uint32 { @@ -10572,7 +10691,7 @@ type GetDraftPolicyRequest struct { func (x *GetDraftPolicyRequest) Reset() { *x = GetDraftPolicyRequest{} - mi := &file_openshell_proto_msgTypes[146] + mi := &file_openshell_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10584,7 +10703,7 @@ func (x *GetDraftPolicyRequest) String() string { func (*GetDraftPolicyRequest) ProtoMessage() {} func (x *GetDraftPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[146] + mi := &file_openshell_proto_msgTypes[148] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10597,7 +10716,7 @@ func (x *GetDraftPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftPolicyRequest.ProtoReflect.Descriptor instead. func (*GetDraftPolicyRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{146} + return file_openshell_proto_rawDescGZIP(), []int{148} } func (x *GetDraftPolicyRequest) GetName() string { @@ -10637,7 +10756,7 @@ type GetDraftPolicyResponse struct { func (x *GetDraftPolicyResponse) Reset() { *x = GetDraftPolicyResponse{} - mi := &file_openshell_proto_msgTypes[147] + mi := &file_openshell_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10649,7 +10768,7 @@ func (x *GetDraftPolicyResponse) String() string { func (*GetDraftPolicyResponse) ProtoMessage() {} func (x *GetDraftPolicyResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[147] + mi := &file_openshell_proto_msgTypes[149] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10662,7 +10781,7 @@ func (x *GetDraftPolicyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftPolicyResponse.ProtoReflect.Descriptor instead. func (*GetDraftPolicyResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{147} + return file_openshell_proto_rawDescGZIP(), []int{149} } func (x *GetDraftPolicyResponse) GetChunks() []*PolicyChunk { @@ -10708,7 +10827,7 @@ type ApproveDraftChunkRequest struct { func (x *ApproveDraftChunkRequest) Reset() { *x = ApproveDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[148] + mi := &file_openshell_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10720,7 +10839,7 @@ func (x *ApproveDraftChunkRequest) String() string { func (*ApproveDraftChunkRequest) ProtoMessage() {} func (x *ApproveDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[148] + mi := &file_openshell_proto_msgTypes[150] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10733,7 +10852,7 @@ func (x *ApproveDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveDraftChunkRequest.ProtoReflect.Descriptor instead. func (*ApproveDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{148} + return file_openshell_proto_rawDescGZIP(), []int{150} } func (x *ApproveDraftChunkRequest) GetName() string { @@ -10769,7 +10888,7 @@ type ApproveDraftChunkResponse struct { func (x *ApproveDraftChunkResponse) Reset() { *x = ApproveDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[149] + mi := &file_openshell_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10781,7 +10900,7 @@ func (x *ApproveDraftChunkResponse) String() string { func (*ApproveDraftChunkResponse) ProtoMessage() {} func (x *ApproveDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[149] + mi := &file_openshell_proto_msgTypes[151] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10794,7 +10913,7 @@ func (x *ApproveDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveDraftChunkResponse.ProtoReflect.Descriptor instead. func (*ApproveDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{149} + return file_openshell_proto_rawDescGZIP(), []int{151} } func (x *ApproveDraftChunkResponse) GetPolicyVersion() uint32 { @@ -10828,7 +10947,7 @@ type RejectDraftChunkRequest struct { func (x *RejectDraftChunkRequest) Reset() { *x = RejectDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[150] + mi := &file_openshell_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10840,7 +10959,7 @@ func (x *RejectDraftChunkRequest) String() string { func (*RejectDraftChunkRequest) ProtoMessage() {} func (x *RejectDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[150] + mi := &file_openshell_proto_msgTypes[152] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10853,7 +10972,7 @@ func (x *RejectDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RejectDraftChunkRequest.ProtoReflect.Descriptor instead. func (*RejectDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{150} + return file_openshell_proto_rawDescGZIP(), []int{152} } func (x *RejectDraftChunkRequest) GetName() string { @@ -10892,7 +11011,7 @@ type RejectDraftChunkResponse struct { func (x *RejectDraftChunkResponse) Reset() { *x = RejectDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[151] + mi := &file_openshell_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10904,7 +11023,7 @@ func (x *RejectDraftChunkResponse) String() string { func (*RejectDraftChunkResponse) ProtoMessage() {} func (x *RejectDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[151] + mi := &file_openshell_proto_msgTypes[153] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10917,7 +11036,7 @@ func (x *RejectDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RejectDraftChunkResponse.ProtoReflect.Descriptor instead. func (*RejectDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{151} + return file_openshell_proto_rawDescGZIP(), []int{153} } // Approve all pending chunks. @@ -10935,7 +11054,7 @@ type ApproveAllDraftChunksRequest struct { func (x *ApproveAllDraftChunksRequest) Reset() { *x = ApproveAllDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[152] + mi := &file_openshell_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10947,7 +11066,7 @@ func (x *ApproveAllDraftChunksRequest) String() string { func (*ApproveAllDraftChunksRequest) ProtoMessage() {} func (x *ApproveAllDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[152] + mi := &file_openshell_proto_msgTypes[154] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10960,7 +11079,7 @@ func (x *ApproveAllDraftChunksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveAllDraftChunksRequest.ProtoReflect.Descriptor instead. func (*ApproveAllDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{152} + return file_openshell_proto_rawDescGZIP(), []int{154} } func (x *ApproveAllDraftChunksRequest) GetName() string { @@ -11000,7 +11119,7 @@ type ApproveAllDraftChunksResponse struct { func (x *ApproveAllDraftChunksResponse) Reset() { *x = ApproveAllDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[153] + mi := &file_openshell_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11012,7 +11131,7 @@ func (x *ApproveAllDraftChunksResponse) String() string { func (*ApproveAllDraftChunksResponse) ProtoMessage() {} func (x *ApproveAllDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[153] + mi := &file_openshell_proto_msgTypes[155] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11025,7 +11144,7 @@ func (x *ApproveAllDraftChunksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveAllDraftChunksResponse.ProtoReflect.Descriptor instead. func (*ApproveAllDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{153} + return file_openshell_proto_rawDescGZIP(), []int{155} } func (x *ApproveAllDraftChunksResponse) GetPolicyVersion() uint32 { @@ -11073,7 +11192,7 @@ type EditDraftChunkRequest struct { func (x *EditDraftChunkRequest) Reset() { *x = EditDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[154] + mi := &file_openshell_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11085,7 +11204,7 @@ func (x *EditDraftChunkRequest) String() string { func (*EditDraftChunkRequest) ProtoMessage() {} func (x *EditDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[154] + mi := &file_openshell_proto_msgTypes[156] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11098,7 +11217,7 @@ func (x *EditDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use EditDraftChunkRequest.ProtoReflect.Descriptor instead. func (*EditDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{154} + return file_openshell_proto_rawDescGZIP(), []int{156} } func (x *EditDraftChunkRequest) GetName() string { @@ -11137,7 +11256,7 @@ type EditDraftChunkResponse struct { func (x *EditDraftChunkResponse) Reset() { *x = EditDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[155] + mi := &file_openshell_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11149,7 +11268,7 @@ func (x *EditDraftChunkResponse) String() string { func (*EditDraftChunkResponse) ProtoMessage() {} func (x *EditDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[155] + mi := &file_openshell_proto_msgTypes[157] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11162,7 +11281,7 @@ func (x *EditDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use EditDraftChunkResponse.ProtoReflect.Descriptor instead. func (*EditDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{155} + return file_openshell_proto_rawDescGZIP(), []int{157} } // Reverse an approval (remove merged rule from active policy). @@ -11180,7 +11299,7 @@ type UndoDraftChunkRequest struct { func (x *UndoDraftChunkRequest) Reset() { *x = UndoDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[156] + mi := &file_openshell_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11192,7 +11311,7 @@ func (x *UndoDraftChunkRequest) String() string { func (*UndoDraftChunkRequest) ProtoMessage() {} func (x *UndoDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[156] + mi := &file_openshell_proto_msgTypes[158] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11205,7 +11324,7 @@ func (x *UndoDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UndoDraftChunkRequest.ProtoReflect.Descriptor instead. func (*UndoDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{156} + return file_openshell_proto_rawDescGZIP(), []int{158} } func (x *UndoDraftChunkRequest) GetName() string { @@ -11241,7 +11360,7 @@ type UndoDraftChunkResponse struct { func (x *UndoDraftChunkResponse) Reset() { *x = UndoDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[157] + mi := &file_openshell_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11253,7 +11372,7 @@ func (x *UndoDraftChunkResponse) String() string { func (*UndoDraftChunkResponse) ProtoMessage() {} func (x *UndoDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[157] + mi := &file_openshell_proto_msgTypes[159] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11266,7 +11385,7 @@ func (x *UndoDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UndoDraftChunkResponse.ProtoReflect.Descriptor instead. func (*UndoDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{157} + return file_openshell_proto_rawDescGZIP(), []int{159} } func (x *UndoDraftChunkResponse) GetPolicyVersion() uint32 { @@ -11296,7 +11415,7 @@ type ClearDraftChunksRequest struct { func (x *ClearDraftChunksRequest) Reset() { *x = ClearDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[158] + mi := &file_openshell_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11308,7 +11427,7 @@ func (x *ClearDraftChunksRequest) String() string { func (*ClearDraftChunksRequest) ProtoMessage() {} func (x *ClearDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[158] + mi := &file_openshell_proto_msgTypes[160] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11321,7 +11440,7 @@ func (x *ClearDraftChunksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ClearDraftChunksRequest.ProtoReflect.Descriptor instead. func (*ClearDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{158} + return file_openshell_proto_rawDescGZIP(), []int{160} } func (x *ClearDraftChunksRequest) GetName() string { @@ -11348,7 +11467,7 @@ type ClearDraftChunksResponse struct { func (x *ClearDraftChunksResponse) Reset() { *x = ClearDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[159] + mi := &file_openshell_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11360,7 +11479,7 @@ func (x *ClearDraftChunksResponse) String() string { func (*ClearDraftChunksResponse) ProtoMessage() {} func (x *ClearDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[159] + mi := &file_openshell_proto_msgTypes[161] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11373,7 +11492,7 @@ func (x *ClearDraftChunksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ClearDraftChunksResponse.ProtoReflect.Descriptor instead. func (*ClearDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{159} + return file_openshell_proto_rawDescGZIP(), []int{161} } func (x *ClearDraftChunksResponse) GetChunksCleared() uint32 { @@ -11396,7 +11515,7 @@ type GetDraftHistoryRequest struct { func (x *GetDraftHistoryRequest) Reset() { *x = GetDraftHistoryRequest{} - mi := &file_openshell_proto_msgTypes[160] + mi := &file_openshell_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11408,7 +11527,7 @@ func (x *GetDraftHistoryRequest) String() string { func (*GetDraftHistoryRequest) ProtoMessage() {} func (x *GetDraftHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[160] + mi := &file_openshell_proto_msgTypes[162] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11421,7 +11540,7 @@ func (x *GetDraftHistoryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftHistoryRequest.ProtoReflect.Descriptor instead. func (*GetDraftHistoryRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{160} + return file_openshell_proto_rawDescGZIP(), []int{162} } func (x *GetDraftHistoryRequest) GetName() string { @@ -11455,7 +11574,7 @@ type DraftHistoryEntry struct { func (x *DraftHistoryEntry) Reset() { *x = DraftHistoryEntry{} - mi := &file_openshell_proto_msgTypes[161] + mi := &file_openshell_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11467,7 +11586,7 @@ func (x *DraftHistoryEntry) String() string { func (*DraftHistoryEntry) ProtoMessage() {} func (x *DraftHistoryEntry) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[161] + mi := &file_openshell_proto_msgTypes[163] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11480,7 +11599,7 @@ func (x *DraftHistoryEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftHistoryEntry.ProtoReflect.Descriptor instead. func (*DraftHistoryEntry) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{161} + return file_openshell_proto_rawDescGZIP(), []int{163} } func (x *DraftHistoryEntry) GetTimestampMs() int64 { @@ -11521,7 +11640,7 @@ type GetDraftHistoryResponse struct { func (x *GetDraftHistoryResponse) Reset() { *x = GetDraftHistoryResponse{} - mi := &file_openshell_proto_msgTypes[162] + mi := &file_openshell_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11533,7 +11652,7 @@ func (x *GetDraftHistoryResponse) String() string { func (*GetDraftHistoryResponse) ProtoMessage() {} func (x *GetDraftHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[162] + mi := &file_openshell_proto_msgTypes[164] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11546,7 +11665,7 @@ func (x *GetDraftHistoryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftHistoryResponse.ProtoReflect.Descriptor instead. func (*GetDraftHistoryResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{162} + return file_openshell_proto_rawDescGZIP(), []int{164} } func (x *GetDraftHistoryResponse) GetEntries() []*DraftHistoryEntry { @@ -11575,7 +11694,7 @@ type PolicyRevisionPayload struct { func (x *PolicyRevisionPayload) Reset() { *x = PolicyRevisionPayload{} - mi := &file_openshell_proto_msgTypes[163] + mi := &file_openshell_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11587,7 +11706,7 @@ func (x *PolicyRevisionPayload) String() string { func (*PolicyRevisionPayload) ProtoMessage() {} func (x *PolicyRevisionPayload) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[163] + mi := &file_openshell_proto_msgTypes[165] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11600,7 +11719,7 @@ func (x *PolicyRevisionPayload) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyRevisionPayload.ProtoReflect.Descriptor instead. func (*PolicyRevisionPayload) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{163} + return file_openshell_proto_rawDescGZIP(), []int{165} } func (x *PolicyRevisionPayload) GetPolicy() *sandboxv1.SandboxPolicy { @@ -11673,7 +11792,7 @@ type DraftChunkPayload struct { func (x *DraftChunkPayload) Reset() { *x = DraftChunkPayload{} - mi := &file_openshell_proto_msgTypes[164] + mi := &file_openshell_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11685,7 +11804,7 @@ func (x *DraftChunkPayload) String() string { func (*DraftChunkPayload) ProtoMessage() {} func (x *DraftChunkPayload) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[164] + mi := &file_openshell_proto_msgTypes[166] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11698,7 +11817,7 @@ func (x *DraftChunkPayload) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftChunkPayload.ProtoReflect.Descriptor instead. func (*DraftChunkPayload) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{164} + return file_openshell_proto_rawDescGZIP(), []int{166} } func (x *DraftChunkPayload) GetRuleName() string { @@ -11804,7 +11923,7 @@ type StoredPolicyRevision struct { func (x *StoredPolicyRevision) Reset() { *x = StoredPolicyRevision{} - mi := &file_openshell_proto_msgTypes[165] + mi := &file_openshell_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11816,7 +11935,7 @@ func (x *StoredPolicyRevision) String() string { func (*StoredPolicyRevision) ProtoMessage() {} func (x *StoredPolicyRevision) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[165] + mi := &file_openshell_proto_msgTypes[167] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11829,7 +11948,7 @@ func (x *StoredPolicyRevision) ProtoReflect() protoreflect.Message { // Deprecated: Use StoredPolicyRevision.ProtoReflect.Descriptor instead. func (*StoredPolicyRevision) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{165} + return file_openshell_proto_rawDescGZIP(), []int{167} } func (x *StoredPolicyRevision) GetId() string { @@ -11932,7 +12051,7 @@ type StoredDraftChunk struct { func (x *StoredDraftChunk) Reset() { *x = StoredDraftChunk{} - mi := &file_openshell_proto_msgTypes[166] + mi := &file_openshell_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11944,7 +12063,7 @@ func (x *StoredDraftChunk) String() string { func (*StoredDraftChunk) ProtoMessage() {} func (x *StoredDraftChunk) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[166] + mi := &file_openshell_proto_msgTypes[168] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11957,7 +12076,7 @@ func (x *StoredDraftChunk) ProtoReflect() protoreflect.Message { // Deprecated: Use StoredDraftChunk.ProtoReflect.Descriptor instead. func (*StoredDraftChunk) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{166} + return file_openshell_proto_rawDescGZIP(), []int{168} } func (x *StoredDraftChunk) GetId() string { @@ -12106,7 +12225,7 @@ type CreateWorkspaceRequest struct { func (x *CreateWorkspaceRequest) Reset() { *x = CreateWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[167] + mi := &file_openshell_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12118,7 +12237,7 @@ func (x *CreateWorkspaceRequest) String() string { func (*CreateWorkspaceRequest) ProtoMessage() {} func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[167] + mi := &file_openshell_proto_msgTypes[169] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12131,7 +12250,7 @@ func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkspaceRequest.ProtoReflect.Descriptor instead. func (*CreateWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{167} + return file_openshell_proto_rawDescGZIP(), []int{169} } func (x *CreateWorkspaceRequest) GetName() string { @@ -12158,7 +12277,7 @@ type CreateWorkspaceResponse struct { func (x *CreateWorkspaceResponse) Reset() { *x = CreateWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[168] + mi := &file_openshell_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12170,7 +12289,7 @@ func (x *CreateWorkspaceResponse) String() string { func (*CreateWorkspaceResponse) ProtoMessage() {} func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[168] + mi := &file_openshell_proto_msgTypes[170] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12183,7 +12302,7 @@ func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkspaceResponse.ProtoReflect.Descriptor instead. func (*CreateWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{168} + return file_openshell_proto_rawDescGZIP(), []int{170} } func (x *CreateWorkspaceResponse) GetWorkspace() *datamodelv1.Workspace { @@ -12204,7 +12323,7 @@ type GetWorkspaceRequest struct { func (x *GetWorkspaceRequest) Reset() { *x = GetWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[169] + mi := &file_openshell_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12216,7 +12335,7 @@ func (x *GetWorkspaceRequest) String() string { func (*GetWorkspaceRequest) ProtoMessage() {} func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[169] + mi := &file_openshell_proto_msgTypes[171] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12229,7 +12348,7 @@ func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkspaceRequest.ProtoReflect.Descriptor instead. func (*GetWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{169} + return file_openshell_proto_rawDescGZIP(), []int{171} } func (x *GetWorkspaceRequest) GetName() string { @@ -12249,7 +12368,7 @@ type GetWorkspaceResponse struct { func (x *GetWorkspaceResponse) Reset() { *x = GetWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[170] + mi := &file_openshell_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12261,7 +12380,7 @@ func (x *GetWorkspaceResponse) String() string { func (*GetWorkspaceResponse) ProtoMessage() {} func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[170] + mi := &file_openshell_proto_msgTypes[172] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12274,7 +12393,7 @@ func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkspaceResponse.ProtoReflect.Descriptor instead. func (*GetWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{170} + return file_openshell_proto_rawDescGZIP(), []int{172} } func (x *GetWorkspaceResponse) GetWorkspace() *datamodelv1.Workspace { @@ -12297,7 +12416,7 @@ type ListWorkspacesRequest struct { func (x *ListWorkspacesRequest) Reset() { *x = ListWorkspacesRequest{} - mi := &file_openshell_proto_msgTypes[171] + mi := &file_openshell_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12309,7 +12428,7 @@ func (x *ListWorkspacesRequest) String() string { func (*ListWorkspacesRequest) ProtoMessage() {} func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[171] + mi := &file_openshell_proto_msgTypes[173] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12322,7 +12441,7 @@ func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspacesRequest.ProtoReflect.Descriptor instead. func (*ListWorkspacesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{171} + return file_openshell_proto_rawDescGZIP(), []int{173} } func (x *ListWorkspacesRequest) GetLimit() uint32 { @@ -12356,7 +12475,7 @@ type ListWorkspacesResponse struct { func (x *ListWorkspacesResponse) Reset() { *x = ListWorkspacesResponse{} - mi := &file_openshell_proto_msgTypes[172] + mi := &file_openshell_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12368,7 +12487,7 @@ func (x *ListWorkspacesResponse) String() string { func (*ListWorkspacesResponse) ProtoMessage() {} func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[172] + mi := &file_openshell_proto_msgTypes[174] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12381,7 +12500,7 @@ func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspacesResponse.ProtoReflect.Descriptor instead. func (*ListWorkspacesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{172} + return file_openshell_proto_rawDescGZIP(), []int{174} } func (x *ListWorkspacesResponse) GetWorkspaces() []*datamodelv1.Workspace { @@ -12402,7 +12521,7 @@ type DeleteWorkspaceRequest struct { func (x *DeleteWorkspaceRequest) Reset() { *x = DeleteWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[173] + mi := &file_openshell_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12414,7 +12533,7 @@ func (x *DeleteWorkspaceRequest) String() string { func (*DeleteWorkspaceRequest) ProtoMessage() {} func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[173] + mi := &file_openshell_proto_msgTypes[175] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12427,7 +12546,7 @@ func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkspaceRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{173} + return file_openshell_proto_rawDescGZIP(), []int{175} } func (x *DeleteWorkspaceRequest) GetName() string { @@ -12447,7 +12566,7 @@ type DeleteWorkspaceResponse struct { func (x *DeleteWorkspaceResponse) Reset() { *x = DeleteWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[174] + mi := &file_openshell_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12459,7 +12578,7 @@ func (x *DeleteWorkspaceResponse) String() string { func (*DeleteWorkspaceResponse) ProtoMessage() {} func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[174] + mi := &file_openshell_proto_msgTypes[176] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12472,7 +12591,7 @@ func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkspaceResponse.ProtoReflect.Descriptor instead. func (*DeleteWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{174} + return file_openshell_proto_rawDescGZIP(), []int{176} } func (x *DeleteWorkspaceResponse) GetDeleted() bool { @@ -12496,7 +12615,7 @@ type WorkspaceMember struct { func (x *WorkspaceMember) Reset() { *x = WorkspaceMember{} - mi := &file_openshell_proto_msgTypes[175] + mi := &file_openshell_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12508,7 +12627,7 @@ func (x *WorkspaceMember) String() string { func (*WorkspaceMember) ProtoMessage() {} func (x *WorkspaceMember) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[175] + mi := &file_openshell_proto_msgTypes[177] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12521,7 +12640,7 @@ func (x *WorkspaceMember) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceMember.ProtoReflect.Descriptor instead. func (*WorkspaceMember) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{175} + return file_openshell_proto_rawDescGZIP(), []int{177} } func (x *WorkspaceMember) GetMetadata() *datamodelv1.ObjectMeta { @@ -12560,7 +12679,7 @@ type AddWorkspaceMemberRequest struct { func (x *AddWorkspaceMemberRequest) Reset() { *x = AddWorkspaceMemberRequest{} - mi := &file_openshell_proto_msgTypes[176] + mi := &file_openshell_proto_msgTypes[178] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12572,7 +12691,7 @@ func (x *AddWorkspaceMemberRequest) String() string { func (*AddWorkspaceMemberRequest) ProtoMessage() {} func (x *AddWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[176] + mi := &file_openshell_proto_msgTypes[178] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12585,7 +12704,7 @@ func (x *AddWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddWorkspaceMemberRequest.ProtoReflect.Descriptor instead. func (*AddWorkspaceMemberRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{176} + return file_openshell_proto_rawDescGZIP(), []int{178} } func (x *AddWorkspaceMemberRequest) GetWorkspace() string { @@ -12619,7 +12738,7 @@ type AddWorkspaceMemberResponse struct { func (x *AddWorkspaceMemberResponse) Reset() { *x = AddWorkspaceMemberResponse{} - mi := &file_openshell_proto_msgTypes[177] + mi := &file_openshell_proto_msgTypes[179] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12631,7 +12750,7 @@ func (x *AddWorkspaceMemberResponse) String() string { func (*AddWorkspaceMemberResponse) ProtoMessage() {} func (x *AddWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[177] + mi := &file_openshell_proto_msgTypes[179] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12644,7 +12763,7 @@ func (x *AddWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddWorkspaceMemberResponse.ProtoReflect.Descriptor instead. func (*AddWorkspaceMemberResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{177} + return file_openshell_proto_rawDescGZIP(), []int{179} } func (x *AddWorkspaceMemberResponse) GetMember() *WorkspaceMember { @@ -12667,7 +12786,7 @@ type RemoveWorkspaceMemberRequest struct { func (x *RemoveWorkspaceMemberRequest) Reset() { *x = RemoveWorkspaceMemberRequest{} - mi := &file_openshell_proto_msgTypes[178] + mi := &file_openshell_proto_msgTypes[180] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12679,7 +12798,7 @@ func (x *RemoveWorkspaceMemberRequest) String() string { func (*RemoveWorkspaceMemberRequest) ProtoMessage() {} func (x *RemoveWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[178] + mi := &file_openshell_proto_msgTypes[180] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12692,7 +12811,7 @@ func (x *RemoveWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveWorkspaceMemberRequest.ProtoReflect.Descriptor instead. func (*RemoveWorkspaceMemberRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{178} + return file_openshell_proto_rawDescGZIP(), []int{180} } func (x *RemoveWorkspaceMemberRequest) GetWorkspace() string { @@ -12719,7 +12838,7 @@ type RemoveWorkspaceMemberResponse struct { func (x *RemoveWorkspaceMemberResponse) Reset() { *x = RemoveWorkspaceMemberResponse{} - mi := &file_openshell_proto_msgTypes[179] + mi := &file_openshell_proto_msgTypes[181] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12731,7 +12850,7 @@ func (x *RemoveWorkspaceMemberResponse) String() string { func (*RemoveWorkspaceMemberResponse) ProtoMessage() {} func (x *RemoveWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[179] + mi := &file_openshell_proto_msgTypes[181] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12744,7 +12863,7 @@ func (x *RemoveWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveWorkspaceMemberResponse.ProtoReflect.Descriptor instead. func (*RemoveWorkspaceMemberResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{179} + return file_openshell_proto_rawDescGZIP(), []int{181} } func (x *RemoveWorkspaceMemberResponse) GetRemoved() bool { @@ -12767,7 +12886,7 @@ type ListWorkspaceMembersRequest struct { func (x *ListWorkspaceMembersRequest) Reset() { *x = ListWorkspaceMembersRequest{} - mi := &file_openshell_proto_msgTypes[180] + mi := &file_openshell_proto_msgTypes[182] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12779,7 +12898,7 @@ func (x *ListWorkspaceMembersRequest) String() string { func (*ListWorkspaceMembersRequest) ProtoMessage() {} func (x *ListWorkspaceMembersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[180] + mi := &file_openshell_proto_msgTypes[182] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12792,7 +12911,7 @@ func (x *ListWorkspaceMembersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspaceMembersRequest.ProtoReflect.Descriptor instead. func (*ListWorkspaceMembersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{180} + return file_openshell_proto_rawDescGZIP(), []int{182} } func (x *ListWorkspaceMembersRequest) GetWorkspace() string { @@ -12826,7 +12945,7 @@ type ListWorkspaceMembersResponse struct { func (x *ListWorkspaceMembersResponse) Reset() { *x = ListWorkspaceMembersResponse{} - mi := &file_openshell_proto_msgTypes[181] + mi := &file_openshell_proto_msgTypes[183] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12838,7 +12957,7 @@ func (x *ListWorkspaceMembersResponse) String() string { func (*ListWorkspaceMembersResponse) ProtoMessage() {} func (x *ListWorkspaceMembersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[181] + mi := &file_openshell_proto_msgTypes[183] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12851,7 +12970,7 @@ func (x *ListWorkspaceMembersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspaceMembersResponse.ProtoReflect.Descriptor instead. func (*ListWorkspaceMembersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{181} + return file_openshell_proto_rawDescGZIP(), []int{183} } func (x *ListWorkspaceMembersResponse) GetMembers() []*WorkspaceMember { @@ -13003,6 +13122,12 @@ const file_openshell_proto_rawDesc = "" + "\tworkspace\x18\x04 \x01(\tR\tworkspace\"H\n" + "\x14DeleteSandboxRequest\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1c\n" + + "\tworkspace\x18\x02 \x01(\tR\tworkspace\"F\n" + + "\x12StopSandboxRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1c\n" + + "\tworkspace\x18\x02 \x01(\tR\tworkspace\"G\n" + + "\x13StartSandboxRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1c\n" + "\tworkspace\x18\x02 \x01(\tR\tworkspace\"B\n" + "\x0fSandboxResponse\x12/\n" + "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxR\asandbox\"L\n" + @@ -13846,14 +13971,17 @@ const file_openshell_proto_rawDesc = "" + "\x05limit\x18\x02 \x01(\rR\x05limit\x12\x16\n" + "\x06offset\x18\x03 \x01(\rR\x06offset\"W\n" + "\x1cListWorkspaceMembersResponse\x127\n" + - "\amembers\x18\x01 \x03(\v2\x1d.openshell.v1.WorkspaceMemberR\amembers*\xb6\x01\n" + + "\amembers\x18\x01 \x03(\v2\x1d.openshell.v1.WorkspaceMemberR\amembers*\x89\x02\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*\xc3\x03\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*\xc3\x03\n" + "!ProviderCredentialRefreshStrategy\x124\n" + "0PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED\x10\x00\x12/\n" + "+PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC\x10\x01\x121\n" + @@ -13885,7 +14013,7 @@ const file_openshell_proto_rawDesc = "" + "\rWorkspaceRole\x12\x1e\n" + "\x1aWORKSPACE_ROLE_UNSPECIFIED\x10\x00\x12\x17\n" + "\x13WORKSPACE_ROLE_USER\x10\x01\x12\x18\n" + - "\x14WORKSPACE_ROLE_ADMIN\x10\x022\xacB\n" + + "\x14WORKSPACE_ROLE_ADMIN\x10\x022\x94D\n" + "\tOpenShell\x12Z\n" + "\x06Health\x12\x1b.openshell.v1.HealthRequest\x1a\x1c.openshell.v1.HealthResponse\"\x15\x82\xb5\x18\x11\n" + "\x0funauthenticated\x12i\n" + @@ -13907,6 +14035,10 @@ const file_openshell_proto_rawDesc = "" + "\x15DetachSandboxProvider\x12*.openshell.v1.DetachSandboxProviderRequest\x1a+.openshell.v1.DetachSandboxProviderResponse\"!\x82\xb5\x18\x1d\n" + "\x06bearer\x12\x04user\"\rsandbox:write\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\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" + @@ -14032,7 +14164,7 @@ func file_openshell_proto_rawDescGZIP() []byte { } var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 206) +var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 208) var file_openshell_proto_goTypes = []any{ (SandboxPhase)(0), // 0: openshell.v1.SandboxPhase (ProviderCredentialRefreshStrategy)(0), // 1: openshell.v1.ProviderCredentialRefreshStrategy @@ -14067,352 +14199,354 @@ var file_openshell_proto_goTypes = []any{ (*AttachSandboxProviderRequest)(nil), // 30: openshell.v1.AttachSandboxProviderRequest (*DetachSandboxProviderRequest)(nil), // 31: openshell.v1.DetachSandboxProviderRequest (*DeleteSandboxRequest)(nil), // 32: openshell.v1.DeleteSandboxRequest - (*SandboxResponse)(nil), // 33: openshell.v1.SandboxResponse - (*ListSandboxesResponse)(nil), // 34: openshell.v1.ListSandboxesResponse - (*ListSandboxProvidersResponse)(nil), // 35: openshell.v1.ListSandboxProvidersResponse - (*AttachSandboxProviderResponse)(nil), // 36: openshell.v1.AttachSandboxProviderResponse - (*DetachSandboxProviderResponse)(nil), // 37: openshell.v1.DetachSandboxProviderResponse - (*DeleteSandboxResponse)(nil), // 38: openshell.v1.DeleteSandboxResponse - (*CreateSshSessionRequest)(nil), // 39: openshell.v1.CreateSshSessionRequest - (*CreateSshSessionResponse)(nil), // 40: openshell.v1.CreateSshSessionResponse - (*ExposeServiceRequest)(nil), // 41: openshell.v1.ExposeServiceRequest - (*GetServiceRequest)(nil), // 42: openshell.v1.GetServiceRequest - (*ListServicesRequest)(nil), // 43: openshell.v1.ListServicesRequest - (*ListServicesResponse)(nil), // 44: openshell.v1.ListServicesResponse - (*DeleteServiceRequest)(nil), // 45: openshell.v1.DeleteServiceRequest - (*DeleteServiceResponse)(nil), // 46: openshell.v1.DeleteServiceResponse - (*ServiceEndpoint)(nil), // 47: openshell.v1.ServiceEndpoint - (*ServiceEndpointResponse)(nil), // 48: openshell.v1.ServiceEndpointResponse - (*RevokeSshSessionRequest)(nil), // 49: openshell.v1.RevokeSshSessionRequest - (*RevokeSshSessionResponse)(nil), // 50: openshell.v1.RevokeSshSessionResponse - (*ExecSandboxRequest)(nil), // 51: openshell.v1.ExecSandboxRequest - (*ExecSandboxStdout)(nil), // 52: openshell.v1.ExecSandboxStdout - (*ExecSandboxStderr)(nil), // 53: openshell.v1.ExecSandboxStderr - (*ExecSandboxExit)(nil), // 54: openshell.v1.ExecSandboxExit - (*ExecSandboxEvent)(nil), // 55: openshell.v1.ExecSandboxEvent - (*TcpForwardInit)(nil), // 56: openshell.v1.TcpForwardInit - (*TcpForwardFrame)(nil), // 57: openshell.v1.TcpForwardFrame - (*ExecSandboxInput)(nil), // 58: openshell.v1.ExecSandboxInput - (*ExecSandboxWindowResize)(nil), // 59: openshell.v1.ExecSandboxWindowResize - (*SshSession)(nil), // 60: openshell.v1.SshSession - (*WatchSandboxRequest)(nil), // 61: openshell.v1.WatchSandboxRequest - (*SandboxStreamEvent)(nil), // 62: openshell.v1.SandboxStreamEvent - (*SandboxLogLine)(nil), // 63: openshell.v1.SandboxLogLine - (*SandboxStreamWarning)(nil), // 64: openshell.v1.SandboxStreamWarning - (*CreateProviderRequest)(nil), // 65: openshell.v1.CreateProviderRequest - (*GetProviderRequest)(nil), // 66: openshell.v1.GetProviderRequest - (*ListProvidersRequest)(nil), // 67: openshell.v1.ListProvidersRequest - (*UpdateProviderRequest)(nil), // 68: openshell.v1.UpdateProviderRequest - (*DeleteProviderRequest)(nil), // 69: openshell.v1.DeleteProviderRequest - (*ProviderResponse)(nil), // 70: openshell.v1.ProviderResponse - (*ListProvidersResponse)(nil), // 71: openshell.v1.ListProvidersResponse - (*ListProviderProfilesRequest)(nil), // 72: openshell.v1.ListProviderProfilesRequest - (*GetProviderProfileRequest)(nil), // 73: openshell.v1.GetProviderProfileRequest - (*ProviderProfileImportItem)(nil), // 74: openshell.v1.ProviderProfileImportItem - (*ProviderProfileDiagnostic)(nil), // 75: openshell.v1.ProviderProfileDiagnostic - (*ProviderCredentialTokenGrantAudienceOverride)(nil), // 76: openshell.v1.ProviderCredentialTokenGrantAudienceOverride - (*ProviderCredentialTokenGrant)(nil), // 77: openshell.v1.ProviderCredentialTokenGrant - (*ProviderProfileCredential)(nil), // 78: openshell.v1.ProviderProfileCredential - (*ProviderCredentialRefreshMaterial)(nil), // 79: openshell.v1.ProviderCredentialRefreshMaterial - (*ProviderCredentialRefreshOutput)(nil), // 80: openshell.v1.ProviderCredentialRefreshOutput - (*ProviderCredentialRefresh)(nil), // 81: openshell.v1.ProviderCredentialRefresh - (*ProviderCredentialRefreshStatus)(nil), // 82: openshell.v1.ProviderCredentialRefreshStatus - (*ProviderProfileDiscovery)(nil), // 83: openshell.v1.ProviderProfileDiscovery - (*StoredProviderCredentialRefreshState)(nil), // 84: openshell.v1.StoredProviderCredentialRefreshState - (*GetProviderRefreshStatusRequest)(nil), // 85: openshell.v1.GetProviderRefreshStatusRequest - (*GetProviderRefreshStatusResponse)(nil), // 86: openshell.v1.GetProviderRefreshStatusResponse - (*ConfigureProviderRefreshRequest)(nil), // 87: openshell.v1.ConfigureProviderRefreshRequest - (*ConfigureProviderRefreshResponse)(nil), // 88: openshell.v1.ConfigureProviderRefreshResponse - (*RotateProviderCredentialRequest)(nil), // 89: openshell.v1.RotateProviderCredentialRequest - (*RotateProviderCredentialResponse)(nil), // 90: openshell.v1.RotateProviderCredentialResponse - (*DeleteProviderRefreshRequest)(nil), // 91: openshell.v1.DeleteProviderRefreshRequest - (*DeleteProviderRefreshResponse)(nil), // 92: openshell.v1.DeleteProviderRefreshResponse - (*ProviderProfile)(nil), // 93: openshell.v1.ProviderProfile - (*StoredProviderProfile)(nil), // 94: openshell.v1.StoredProviderProfile - (*ProviderProfileResponse)(nil), // 95: openshell.v1.ProviderProfileResponse - (*ListProviderProfilesResponse)(nil), // 96: openshell.v1.ListProviderProfilesResponse - (*ImportProviderProfilesRequest)(nil), // 97: openshell.v1.ImportProviderProfilesRequest - (*ImportProviderProfilesResponse)(nil), // 98: openshell.v1.ImportProviderProfilesResponse - (*UpdateProviderProfilesRequest)(nil), // 99: openshell.v1.UpdateProviderProfilesRequest - (*UpdateProviderProfilesResponse)(nil), // 100: openshell.v1.UpdateProviderProfilesResponse - (*LintProviderProfilesRequest)(nil), // 101: openshell.v1.LintProviderProfilesRequest - (*LintProviderProfilesResponse)(nil), // 102: openshell.v1.LintProviderProfilesResponse - (*DeleteProviderResponse)(nil), // 103: openshell.v1.DeleteProviderResponse - (*DeleteProviderProfileRequest)(nil), // 104: openshell.v1.DeleteProviderProfileRequest - (*DeleteProviderProfileResponse)(nil), // 105: openshell.v1.DeleteProviderProfileResponse - (*GetSandboxProviderEnvironmentRequest)(nil), // 106: openshell.v1.GetSandboxProviderEnvironmentRequest - (*StaticCredentialEndpointBinding)(nil), // 107: openshell.v1.StaticCredentialEndpointBinding - (*StaticCredentialBinding)(nil), // 108: openshell.v1.StaticCredentialBinding - (*GetSandboxProviderEnvironmentResponse)(nil), // 109: openshell.v1.GetSandboxProviderEnvironmentResponse - (*UpdateConfigRequest)(nil), // 110: openshell.v1.UpdateConfigRequest - (*PolicyMergeOperation)(nil), // 111: openshell.v1.PolicyMergeOperation - (*AddNetworkRule)(nil), // 112: openshell.v1.AddNetworkRule - (*RemoveNetworkEndpoint)(nil), // 113: openshell.v1.RemoveNetworkEndpoint - (*RemoveNetworkRule)(nil), // 114: openshell.v1.RemoveNetworkRule - (*AddDenyRules)(nil), // 115: openshell.v1.AddDenyRules - (*AddAllowRules)(nil), // 116: openshell.v1.AddAllowRules - (*RemoveNetworkBinary)(nil), // 117: openshell.v1.RemoveNetworkBinary - (*UpdateConfigResponse)(nil), // 118: openshell.v1.UpdateConfigResponse - (*GetSandboxPolicyStatusRequest)(nil), // 119: openshell.v1.GetSandboxPolicyStatusRequest - (*GetSandboxPolicyStatusResponse)(nil), // 120: openshell.v1.GetSandboxPolicyStatusResponse - (*ListSandboxPoliciesRequest)(nil), // 121: openshell.v1.ListSandboxPoliciesRequest - (*ListSandboxPoliciesResponse)(nil), // 122: openshell.v1.ListSandboxPoliciesResponse - (*ReportPolicyStatusRequest)(nil), // 123: openshell.v1.ReportPolicyStatusRequest - (*ReportPolicyStatusResponse)(nil), // 124: openshell.v1.ReportPolicyStatusResponse - (*SandboxPolicyRevision)(nil), // 125: openshell.v1.SandboxPolicyRevision - (*GetSandboxLogsRequest)(nil), // 126: openshell.v1.GetSandboxLogsRequest - (*PushSandboxLogsRequest)(nil), // 127: openshell.v1.PushSandboxLogsRequest - (*PushSandboxLogsResponse)(nil), // 128: openshell.v1.PushSandboxLogsResponse - (*GetSandboxLogsResponse)(nil), // 129: openshell.v1.GetSandboxLogsResponse - (*SupervisorMessage)(nil), // 130: openshell.v1.SupervisorMessage - (*GatewayMessage)(nil), // 131: openshell.v1.GatewayMessage - (*SupervisorHello)(nil), // 132: openshell.v1.SupervisorHello - (*SessionAccepted)(nil), // 133: openshell.v1.SessionAccepted - (*SessionRejected)(nil), // 134: openshell.v1.SessionRejected - (*SupervisorHeartbeat)(nil), // 135: openshell.v1.SupervisorHeartbeat - (*GatewayHeartbeat)(nil), // 136: openshell.v1.GatewayHeartbeat - (*RelayOpen)(nil), // 137: openshell.v1.RelayOpen - (*SshRelayTarget)(nil), // 138: openshell.v1.SshRelayTarget - (*TcpRelayTarget)(nil), // 139: openshell.v1.TcpRelayTarget - (*RelayInit)(nil), // 140: openshell.v1.RelayInit - (*RelayFrame)(nil), // 141: openshell.v1.RelayFrame - (*RelayOpenResult)(nil), // 142: openshell.v1.RelayOpenResult - (*RelayClose)(nil), // 143: openshell.v1.RelayClose - (*L7RequestSample)(nil), // 144: openshell.v1.L7RequestSample - (*DenialSummary)(nil), // 145: openshell.v1.DenialSummary - (*DenialGroupCount)(nil), // 146: openshell.v1.DenialGroupCount - (*NetworkActivitySummary)(nil), // 147: openshell.v1.NetworkActivitySummary - (*PolicyChunk)(nil), // 148: openshell.v1.PolicyChunk - (*DraftPolicyUpdate)(nil), // 149: openshell.v1.DraftPolicyUpdate - (*SubmitPolicyAnalysisRequest)(nil), // 150: openshell.v1.SubmitPolicyAnalysisRequest - (*SubmitPolicyAnalysisResponse)(nil), // 151: openshell.v1.SubmitPolicyAnalysisResponse - (*GetDraftPolicyRequest)(nil), // 152: openshell.v1.GetDraftPolicyRequest - (*GetDraftPolicyResponse)(nil), // 153: openshell.v1.GetDraftPolicyResponse - (*ApproveDraftChunkRequest)(nil), // 154: openshell.v1.ApproveDraftChunkRequest - (*ApproveDraftChunkResponse)(nil), // 155: openshell.v1.ApproveDraftChunkResponse - (*RejectDraftChunkRequest)(nil), // 156: openshell.v1.RejectDraftChunkRequest - (*RejectDraftChunkResponse)(nil), // 157: openshell.v1.RejectDraftChunkResponse - (*ApproveAllDraftChunksRequest)(nil), // 158: openshell.v1.ApproveAllDraftChunksRequest - (*ApproveAllDraftChunksResponse)(nil), // 159: openshell.v1.ApproveAllDraftChunksResponse - (*EditDraftChunkRequest)(nil), // 160: openshell.v1.EditDraftChunkRequest - (*EditDraftChunkResponse)(nil), // 161: openshell.v1.EditDraftChunkResponse - (*UndoDraftChunkRequest)(nil), // 162: openshell.v1.UndoDraftChunkRequest - (*UndoDraftChunkResponse)(nil), // 163: openshell.v1.UndoDraftChunkResponse - (*ClearDraftChunksRequest)(nil), // 164: openshell.v1.ClearDraftChunksRequest - (*ClearDraftChunksResponse)(nil), // 165: openshell.v1.ClearDraftChunksResponse - (*GetDraftHistoryRequest)(nil), // 166: openshell.v1.GetDraftHistoryRequest - (*DraftHistoryEntry)(nil), // 167: openshell.v1.DraftHistoryEntry - (*GetDraftHistoryResponse)(nil), // 168: openshell.v1.GetDraftHistoryResponse - (*PolicyRevisionPayload)(nil), // 169: openshell.v1.PolicyRevisionPayload - (*DraftChunkPayload)(nil), // 170: openshell.v1.DraftChunkPayload - (*StoredPolicyRevision)(nil), // 171: openshell.v1.StoredPolicyRevision - (*StoredDraftChunk)(nil), // 172: openshell.v1.StoredDraftChunk - (*CreateWorkspaceRequest)(nil), // 173: openshell.v1.CreateWorkspaceRequest - (*CreateWorkspaceResponse)(nil), // 174: openshell.v1.CreateWorkspaceResponse - (*GetWorkspaceRequest)(nil), // 175: openshell.v1.GetWorkspaceRequest - (*GetWorkspaceResponse)(nil), // 176: openshell.v1.GetWorkspaceResponse - (*ListWorkspacesRequest)(nil), // 177: openshell.v1.ListWorkspacesRequest - (*ListWorkspacesResponse)(nil), // 178: openshell.v1.ListWorkspacesResponse - (*DeleteWorkspaceRequest)(nil), // 179: openshell.v1.DeleteWorkspaceRequest - (*DeleteWorkspaceResponse)(nil), // 180: openshell.v1.DeleteWorkspaceResponse - (*WorkspaceMember)(nil), // 181: openshell.v1.WorkspaceMember - (*AddWorkspaceMemberRequest)(nil), // 182: openshell.v1.AddWorkspaceMemberRequest - (*AddWorkspaceMemberResponse)(nil), // 183: openshell.v1.AddWorkspaceMemberResponse - (*RemoveWorkspaceMemberRequest)(nil), // 184: openshell.v1.RemoveWorkspaceMemberRequest - (*RemoveWorkspaceMemberResponse)(nil), // 185: openshell.v1.RemoveWorkspaceMemberResponse - (*ListWorkspaceMembersRequest)(nil), // 186: openshell.v1.ListWorkspaceMembersRequest - (*ListWorkspaceMembersResponse)(nil), // 187: openshell.v1.ListWorkspaceMembersResponse - nil, // 188: openshell.v1.SandboxSpec.EnvironmentEntry - nil, // 189: openshell.v1.SandboxTemplate.LabelsEntry - nil, // 190: openshell.v1.SandboxTemplate.AnnotationsEntry - nil, // 191: openshell.v1.SandboxTemplate.EnvironmentEntry - nil, // 192: openshell.v1.PlatformEvent.MetadataEntry - nil, // 193: openshell.v1.CreateSandboxRequest.LabelsEntry - nil, // 194: openshell.v1.CreateSandboxRequest.AnnotationsEntry - nil, // 195: openshell.v1.ExecSandboxRequest.EnvironmentEntry - nil, // 196: openshell.v1.SandboxLogLine.FieldsEntry - nil, // 197: openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry - nil, // 198: openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry - nil, // 199: openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry - nil, // 200: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - nil, // 201: openshell.v1.ProviderProfile.AnnotationsEntry - nil, // 202: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - nil, // 203: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry - nil, // 204: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - nil, // 205: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - nil, // 206: openshell.v1.UpdateConfigRequest.AnnotationsEntry - nil, // 207: openshell.v1.UpdateConfigResponse.AnnotationsEntry - nil, // 208: openshell.v1.SandboxPolicyRevision.ProvenanceEntry - nil, // 209: openshell.v1.PolicyRevisionPayload.ProvenanceEntry - nil, // 210: openshell.v1.StoredPolicyRevision.ProvenanceEntry - nil, // 211: openshell.v1.CreateWorkspaceRequest.LabelsEntry - (*datamodelv1.ObjectMeta)(nil), // 212: openshell.datamodel.v1.ObjectMeta - (*sandboxv1.SandboxPolicy)(nil), // 213: openshell.sandbox.v1.SandboxPolicy - (*structpb.Struct)(nil), // 214: google.protobuf.Struct - (*datamodelv1.Provider)(nil), // 215: openshell.datamodel.v1.Provider - (*sandboxv1.NetworkEndpoint)(nil), // 216: openshell.sandbox.v1.NetworkEndpoint - (*sandboxv1.NetworkBinary)(nil), // 217: openshell.sandbox.v1.NetworkBinary - (*sandboxv1.SettingValue)(nil), // 218: openshell.sandbox.v1.SettingValue - (*sandboxv1.NetworkPolicyRule)(nil), // 219: openshell.sandbox.v1.NetworkPolicyRule - (*sandboxv1.L7DenyRule)(nil), // 220: openshell.sandbox.v1.L7DenyRule - (*sandboxv1.L7Rule)(nil), // 221: openshell.sandbox.v1.L7Rule - (*datamodelv1.Workspace)(nil), // 222: openshell.datamodel.v1.Workspace - (*sandboxv1.GetSandboxConfigRequest)(nil), // 223: openshell.sandbox.v1.GetSandboxConfigRequest - (*sandboxv1.GetGatewayConfigRequest)(nil), // 224: openshell.sandbox.v1.GetGatewayConfigRequest - (*sandboxv1.GetSandboxConfigResponse)(nil), // 225: openshell.sandbox.v1.GetSandboxConfigResponse - (*sandboxv1.GetGatewayConfigResponse)(nil), // 226: openshell.sandbox.v1.GetGatewayConfigResponse + (*StopSandboxRequest)(nil), // 33: openshell.v1.StopSandboxRequest + (*StartSandboxRequest)(nil), // 34: openshell.v1.StartSandboxRequest + (*SandboxResponse)(nil), // 35: openshell.v1.SandboxResponse + (*ListSandboxesResponse)(nil), // 36: openshell.v1.ListSandboxesResponse + (*ListSandboxProvidersResponse)(nil), // 37: openshell.v1.ListSandboxProvidersResponse + (*AttachSandboxProviderResponse)(nil), // 38: openshell.v1.AttachSandboxProviderResponse + (*DetachSandboxProviderResponse)(nil), // 39: openshell.v1.DetachSandboxProviderResponse + (*DeleteSandboxResponse)(nil), // 40: openshell.v1.DeleteSandboxResponse + (*CreateSshSessionRequest)(nil), // 41: openshell.v1.CreateSshSessionRequest + (*CreateSshSessionResponse)(nil), // 42: openshell.v1.CreateSshSessionResponse + (*ExposeServiceRequest)(nil), // 43: openshell.v1.ExposeServiceRequest + (*GetServiceRequest)(nil), // 44: openshell.v1.GetServiceRequest + (*ListServicesRequest)(nil), // 45: openshell.v1.ListServicesRequest + (*ListServicesResponse)(nil), // 46: openshell.v1.ListServicesResponse + (*DeleteServiceRequest)(nil), // 47: openshell.v1.DeleteServiceRequest + (*DeleteServiceResponse)(nil), // 48: openshell.v1.DeleteServiceResponse + (*ServiceEndpoint)(nil), // 49: openshell.v1.ServiceEndpoint + (*ServiceEndpointResponse)(nil), // 50: openshell.v1.ServiceEndpointResponse + (*RevokeSshSessionRequest)(nil), // 51: openshell.v1.RevokeSshSessionRequest + (*RevokeSshSessionResponse)(nil), // 52: openshell.v1.RevokeSshSessionResponse + (*ExecSandboxRequest)(nil), // 53: openshell.v1.ExecSandboxRequest + (*ExecSandboxStdout)(nil), // 54: openshell.v1.ExecSandboxStdout + (*ExecSandboxStderr)(nil), // 55: openshell.v1.ExecSandboxStderr + (*ExecSandboxExit)(nil), // 56: openshell.v1.ExecSandboxExit + (*ExecSandboxEvent)(nil), // 57: openshell.v1.ExecSandboxEvent + (*TcpForwardInit)(nil), // 58: openshell.v1.TcpForwardInit + (*TcpForwardFrame)(nil), // 59: openshell.v1.TcpForwardFrame + (*ExecSandboxInput)(nil), // 60: openshell.v1.ExecSandboxInput + (*ExecSandboxWindowResize)(nil), // 61: openshell.v1.ExecSandboxWindowResize + (*SshSession)(nil), // 62: openshell.v1.SshSession + (*WatchSandboxRequest)(nil), // 63: openshell.v1.WatchSandboxRequest + (*SandboxStreamEvent)(nil), // 64: openshell.v1.SandboxStreamEvent + (*SandboxLogLine)(nil), // 65: openshell.v1.SandboxLogLine + (*SandboxStreamWarning)(nil), // 66: openshell.v1.SandboxStreamWarning + (*CreateProviderRequest)(nil), // 67: openshell.v1.CreateProviderRequest + (*GetProviderRequest)(nil), // 68: openshell.v1.GetProviderRequest + (*ListProvidersRequest)(nil), // 69: openshell.v1.ListProvidersRequest + (*UpdateProviderRequest)(nil), // 70: openshell.v1.UpdateProviderRequest + (*DeleteProviderRequest)(nil), // 71: openshell.v1.DeleteProviderRequest + (*ProviderResponse)(nil), // 72: openshell.v1.ProviderResponse + (*ListProvidersResponse)(nil), // 73: openshell.v1.ListProvidersResponse + (*ListProviderProfilesRequest)(nil), // 74: openshell.v1.ListProviderProfilesRequest + (*GetProviderProfileRequest)(nil), // 75: openshell.v1.GetProviderProfileRequest + (*ProviderProfileImportItem)(nil), // 76: openshell.v1.ProviderProfileImportItem + (*ProviderProfileDiagnostic)(nil), // 77: openshell.v1.ProviderProfileDiagnostic + (*ProviderCredentialTokenGrantAudienceOverride)(nil), // 78: openshell.v1.ProviderCredentialTokenGrantAudienceOverride + (*ProviderCredentialTokenGrant)(nil), // 79: openshell.v1.ProviderCredentialTokenGrant + (*ProviderProfileCredential)(nil), // 80: openshell.v1.ProviderProfileCredential + (*ProviderCredentialRefreshMaterial)(nil), // 81: openshell.v1.ProviderCredentialRefreshMaterial + (*ProviderCredentialRefreshOutput)(nil), // 82: openshell.v1.ProviderCredentialRefreshOutput + (*ProviderCredentialRefresh)(nil), // 83: openshell.v1.ProviderCredentialRefresh + (*ProviderCredentialRefreshStatus)(nil), // 84: openshell.v1.ProviderCredentialRefreshStatus + (*ProviderProfileDiscovery)(nil), // 85: openshell.v1.ProviderProfileDiscovery + (*StoredProviderCredentialRefreshState)(nil), // 86: openshell.v1.StoredProviderCredentialRefreshState + (*GetProviderRefreshStatusRequest)(nil), // 87: openshell.v1.GetProviderRefreshStatusRequest + (*GetProviderRefreshStatusResponse)(nil), // 88: openshell.v1.GetProviderRefreshStatusResponse + (*ConfigureProviderRefreshRequest)(nil), // 89: openshell.v1.ConfigureProviderRefreshRequest + (*ConfigureProviderRefreshResponse)(nil), // 90: openshell.v1.ConfigureProviderRefreshResponse + (*RotateProviderCredentialRequest)(nil), // 91: openshell.v1.RotateProviderCredentialRequest + (*RotateProviderCredentialResponse)(nil), // 92: openshell.v1.RotateProviderCredentialResponse + (*DeleteProviderRefreshRequest)(nil), // 93: openshell.v1.DeleteProviderRefreshRequest + (*DeleteProviderRefreshResponse)(nil), // 94: openshell.v1.DeleteProviderRefreshResponse + (*ProviderProfile)(nil), // 95: openshell.v1.ProviderProfile + (*StoredProviderProfile)(nil), // 96: openshell.v1.StoredProviderProfile + (*ProviderProfileResponse)(nil), // 97: openshell.v1.ProviderProfileResponse + (*ListProviderProfilesResponse)(nil), // 98: openshell.v1.ListProviderProfilesResponse + (*ImportProviderProfilesRequest)(nil), // 99: openshell.v1.ImportProviderProfilesRequest + (*ImportProviderProfilesResponse)(nil), // 100: openshell.v1.ImportProviderProfilesResponse + (*UpdateProviderProfilesRequest)(nil), // 101: openshell.v1.UpdateProviderProfilesRequest + (*UpdateProviderProfilesResponse)(nil), // 102: openshell.v1.UpdateProviderProfilesResponse + (*LintProviderProfilesRequest)(nil), // 103: openshell.v1.LintProviderProfilesRequest + (*LintProviderProfilesResponse)(nil), // 104: openshell.v1.LintProviderProfilesResponse + (*DeleteProviderResponse)(nil), // 105: openshell.v1.DeleteProviderResponse + (*DeleteProviderProfileRequest)(nil), // 106: openshell.v1.DeleteProviderProfileRequest + (*DeleteProviderProfileResponse)(nil), // 107: openshell.v1.DeleteProviderProfileResponse + (*GetSandboxProviderEnvironmentRequest)(nil), // 108: openshell.v1.GetSandboxProviderEnvironmentRequest + (*StaticCredentialEndpointBinding)(nil), // 109: openshell.v1.StaticCredentialEndpointBinding + (*StaticCredentialBinding)(nil), // 110: openshell.v1.StaticCredentialBinding + (*GetSandboxProviderEnvironmentResponse)(nil), // 111: openshell.v1.GetSandboxProviderEnvironmentResponse + (*UpdateConfigRequest)(nil), // 112: openshell.v1.UpdateConfigRequest + (*PolicyMergeOperation)(nil), // 113: openshell.v1.PolicyMergeOperation + (*AddNetworkRule)(nil), // 114: openshell.v1.AddNetworkRule + (*RemoveNetworkEndpoint)(nil), // 115: openshell.v1.RemoveNetworkEndpoint + (*RemoveNetworkRule)(nil), // 116: openshell.v1.RemoveNetworkRule + (*AddDenyRules)(nil), // 117: openshell.v1.AddDenyRules + (*AddAllowRules)(nil), // 118: openshell.v1.AddAllowRules + (*RemoveNetworkBinary)(nil), // 119: openshell.v1.RemoveNetworkBinary + (*UpdateConfigResponse)(nil), // 120: openshell.v1.UpdateConfigResponse + (*GetSandboxPolicyStatusRequest)(nil), // 121: openshell.v1.GetSandboxPolicyStatusRequest + (*GetSandboxPolicyStatusResponse)(nil), // 122: openshell.v1.GetSandboxPolicyStatusResponse + (*ListSandboxPoliciesRequest)(nil), // 123: openshell.v1.ListSandboxPoliciesRequest + (*ListSandboxPoliciesResponse)(nil), // 124: openshell.v1.ListSandboxPoliciesResponse + (*ReportPolicyStatusRequest)(nil), // 125: openshell.v1.ReportPolicyStatusRequest + (*ReportPolicyStatusResponse)(nil), // 126: openshell.v1.ReportPolicyStatusResponse + (*SandboxPolicyRevision)(nil), // 127: openshell.v1.SandboxPolicyRevision + (*GetSandboxLogsRequest)(nil), // 128: openshell.v1.GetSandboxLogsRequest + (*PushSandboxLogsRequest)(nil), // 129: openshell.v1.PushSandboxLogsRequest + (*PushSandboxLogsResponse)(nil), // 130: openshell.v1.PushSandboxLogsResponse + (*GetSandboxLogsResponse)(nil), // 131: openshell.v1.GetSandboxLogsResponse + (*SupervisorMessage)(nil), // 132: openshell.v1.SupervisorMessage + (*GatewayMessage)(nil), // 133: openshell.v1.GatewayMessage + (*SupervisorHello)(nil), // 134: openshell.v1.SupervisorHello + (*SessionAccepted)(nil), // 135: openshell.v1.SessionAccepted + (*SessionRejected)(nil), // 136: openshell.v1.SessionRejected + (*SupervisorHeartbeat)(nil), // 137: openshell.v1.SupervisorHeartbeat + (*GatewayHeartbeat)(nil), // 138: openshell.v1.GatewayHeartbeat + (*RelayOpen)(nil), // 139: openshell.v1.RelayOpen + (*SshRelayTarget)(nil), // 140: openshell.v1.SshRelayTarget + (*TcpRelayTarget)(nil), // 141: openshell.v1.TcpRelayTarget + (*RelayInit)(nil), // 142: openshell.v1.RelayInit + (*RelayFrame)(nil), // 143: openshell.v1.RelayFrame + (*RelayOpenResult)(nil), // 144: openshell.v1.RelayOpenResult + (*RelayClose)(nil), // 145: openshell.v1.RelayClose + (*L7RequestSample)(nil), // 146: openshell.v1.L7RequestSample + (*DenialSummary)(nil), // 147: openshell.v1.DenialSummary + (*DenialGroupCount)(nil), // 148: openshell.v1.DenialGroupCount + (*NetworkActivitySummary)(nil), // 149: openshell.v1.NetworkActivitySummary + (*PolicyChunk)(nil), // 150: openshell.v1.PolicyChunk + (*DraftPolicyUpdate)(nil), // 151: openshell.v1.DraftPolicyUpdate + (*SubmitPolicyAnalysisRequest)(nil), // 152: openshell.v1.SubmitPolicyAnalysisRequest + (*SubmitPolicyAnalysisResponse)(nil), // 153: openshell.v1.SubmitPolicyAnalysisResponse + (*GetDraftPolicyRequest)(nil), // 154: openshell.v1.GetDraftPolicyRequest + (*GetDraftPolicyResponse)(nil), // 155: openshell.v1.GetDraftPolicyResponse + (*ApproveDraftChunkRequest)(nil), // 156: openshell.v1.ApproveDraftChunkRequest + (*ApproveDraftChunkResponse)(nil), // 157: openshell.v1.ApproveDraftChunkResponse + (*RejectDraftChunkRequest)(nil), // 158: openshell.v1.RejectDraftChunkRequest + (*RejectDraftChunkResponse)(nil), // 159: openshell.v1.RejectDraftChunkResponse + (*ApproveAllDraftChunksRequest)(nil), // 160: openshell.v1.ApproveAllDraftChunksRequest + (*ApproveAllDraftChunksResponse)(nil), // 161: openshell.v1.ApproveAllDraftChunksResponse + (*EditDraftChunkRequest)(nil), // 162: openshell.v1.EditDraftChunkRequest + (*EditDraftChunkResponse)(nil), // 163: openshell.v1.EditDraftChunkResponse + (*UndoDraftChunkRequest)(nil), // 164: openshell.v1.UndoDraftChunkRequest + (*UndoDraftChunkResponse)(nil), // 165: openshell.v1.UndoDraftChunkResponse + (*ClearDraftChunksRequest)(nil), // 166: openshell.v1.ClearDraftChunksRequest + (*ClearDraftChunksResponse)(nil), // 167: openshell.v1.ClearDraftChunksResponse + (*GetDraftHistoryRequest)(nil), // 168: openshell.v1.GetDraftHistoryRequest + (*DraftHistoryEntry)(nil), // 169: openshell.v1.DraftHistoryEntry + (*GetDraftHistoryResponse)(nil), // 170: openshell.v1.GetDraftHistoryResponse + (*PolicyRevisionPayload)(nil), // 171: openshell.v1.PolicyRevisionPayload + (*DraftChunkPayload)(nil), // 172: openshell.v1.DraftChunkPayload + (*StoredPolicyRevision)(nil), // 173: openshell.v1.StoredPolicyRevision + (*StoredDraftChunk)(nil), // 174: openshell.v1.StoredDraftChunk + (*CreateWorkspaceRequest)(nil), // 175: openshell.v1.CreateWorkspaceRequest + (*CreateWorkspaceResponse)(nil), // 176: openshell.v1.CreateWorkspaceResponse + (*GetWorkspaceRequest)(nil), // 177: openshell.v1.GetWorkspaceRequest + (*GetWorkspaceResponse)(nil), // 178: openshell.v1.GetWorkspaceResponse + (*ListWorkspacesRequest)(nil), // 179: openshell.v1.ListWorkspacesRequest + (*ListWorkspacesResponse)(nil), // 180: openshell.v1.ListWorkspacesResponse + (*DeleteWorkspaceRequest)(nil), // 181: openshell.v1.DeleteWorkspaceRequest + (*DeleteWorkspaceResponse)(nil), // 182: openshell.v1.DeleteWorkspaceResponse + (*WorkspaceMember)(nil), // 183: openshell.v1.WorkspaceMember + (*AddWorkspaceMemberRequest)(nil), // 184: openshell.v1.AddWorkspaceMemberRequest + (*AddWorkspaceMemberResponse)(nil), // 185: openshell.v1.AddWorkspaceMemberResponse + (*RemoveWorkspaceMemberRequest)(nil), // 186: openshell.v1.RemoveWorkspaceMemberRequest + (*RemoveWorkspaceMemberResponse)(nil), // 187: openshell.v1.RemoveWorkspaceMemberResponse + (*ListWorkspaceMembersRequest)(nil), // 188: openshell.v1.ListWorkspaceMembersRequest + (*ListWorkspaceMembersResponse)(nil), // 189: openshell.v1.ListWorkspaceMembersResponse + nil, // 190: openshell.v1.SandboxSpec.EnvironmentEntry + nil, // 191: openshell.v1.SandboxTemplate.LabelsEntry + nil, // 192: openshell.v1.SandboxTemplate.AnnotationsEntry + nil, // 193: openshell.v1.SandboxTemplate.EnvironmentEntry + nil, // 194: openshell.v1.PlatformEvent.MetadataEntry + nil, // 195: openshell.v1.CreateSandboxRequest.LabelsEntry + nil, // 196: openshell.v1.CreateSandboxRequest.AnnotationsEntry + nil, // 197: openshell.v1.ExecSandboxRequest.EnvironmentEntry + nil, // 198: openshell.v1.SandboxLogLine.FieldsEntry + nil, // 199: openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry + nil, // 200: openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry + nil, // 201: openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry + nil, // 202: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + nil, // 203: openshell.v1.ProviderProfile.AnnotationsEntry + nil, // 204: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry + nil, // 205: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry + nil, // 206: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry + nil, // 207: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry + nil, // 208: openshell.v1.UpdateConfigRequest.AnnotationsEntry + nil, // 209: openshell.v1.UpdateConfigResponse.AnnotationsEntry + nil, // 210: openshell.v1.SandboxPolicyRevision.ProvenanceEntry + nil, // 211: openshell.v1.PolicyRevisionPayload.ProvenanceEntry + nil, // 212: openshell.v1.StoredPolicyRevision.ProvenanceEntry + nil, // 213: openshell.v1.CreateWorkspaceRequest.LabelsEntry + (*datamodelv1.ObjectMeta)(nil), // 214: openshell.datamodel.v1.ObjectMeta + (*sandboxv1.SandboxPolicy)(nil), // 215: openshell.sandbox.v1.SandboxPolicy + (*structpb.Struct)(nil), // 216: google.protobuf.Struct + (*datamodelv1.Provider)(nil), // 217: openshell.datamodel.v1.Provider + (*sandboxv1.NetworkEndpoint)(nil), // 218: openshell.sandbox.v1.NetworkEndpoint + (*sandboxv1.NetworkBinary)(nil), // 219: openshell.sandbox.v1.NetworkBinary + (*sandboxv1.SettingValue)(nil), // 220: openshell.sandbox.v1.SettingValue + (*sandboxv1.NetworkPolicyRule)(nil), // 221: openshell.sandbox.v1.NetworkPolicyRule + (*sandboxv1.L7DenyRule)(nil), // 222: openshell.sandbox.v1.L7DenyRule + (*sandboxv1.L7Rule)(nil), // 223: openshell.sandbox.v1.L7Rule + (*datamodelv1.Workspace)(nil), // 224: openshell.datamodel.v1.Workspace + (*sandboxv1.GetSandboxConfigRequest)(nil), // 225: openshell.sandbox.v1.GetSandboxConfigRequest + (*sandboxv1.GetGatewayConfigRequest)(nil), // 226: openshell.sandbox.v1.GetGatewayConfigRequest + (*sandboxv1.GetSandboxConfigResponse)(nil), // 227: openshell.sandbox.v1.GetSandboxConfigResponse + (*sandboxv1.GetGatewayConfigResponse)(nil), // 228: openshell.sandbox.v1.GetGatewayConfigResponse } var file_openshell_proto_depIdxs = []int32{ 4, // 0: openshell.v1.HealthResponse.status:type_name -> openshell.v1.ServiceStatus 4, // 1: openshell.v1.GetGatewayInfoResponse.status:type_name -> openshell.v1.ServiceStatus 16, // 2: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo 17, // 3: openshell.v1.ComputeDriverInfo.capabilities:type_name -> openshell.v1.ComputeDriverCapabilities - 212, // 4: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 214, // 4: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 19, // 5: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec 23, // 6: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus - 188, // 7: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry + 190, // 7: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry 22, // 8: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate - 213, // 9: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 215, // 9: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy 20, // 10: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements 21, // 11: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements - 189, // 12: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry - 190, // 13: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry - 191, // 14: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry - 214, // 15: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct - 214, // 16: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct + 191, // 12: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry + 192, // 13: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry + 193, // 14: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry + 216, // 15: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct + 216, // 16: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct 24, // 17: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition 0, // 18: openshell.v1.SandboxStatus.phase:type_name -> openshell.v1.SandboxPhase - 192, // 19: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry + 194, // 19: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry 19, // 20: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec - 193, // 21: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry - 194, // 22: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry + 195, // 21: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry + 196, // 22: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry 18, // 23: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox 18, // 24: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox - 215, // 25: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 217, // 25: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider 18, // 26: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox 18, // 27: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 48, // 28: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse - 212, // 29: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 47, // 30: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint - 195, // 31: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry - 52, // 32: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout - 53, // 33: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr - 54, // 34: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit - 138, // 35: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget - 139, // 36: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget - 56, // 37: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit - 51, // 38: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest - 59, // 39: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize - 212, // 40: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 50, // 28: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse + 214, // 29: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 49, // 30: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint + 197, // 31: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry + 54, // 32: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout + 55, // 33: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr + 56, // 34: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit + 140, // 35: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget + 141, // 36: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget + 58, // 37: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit + 53, // 38: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest + 61, // 39: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize + 214, // 40: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 18, // 41: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox - 63, // 42: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine + 65, // 42: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine 25, // 43: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent - 64, // 44: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning - 149, // 45: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate - 196, // 46: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry - 215, // 47: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 215, // 48: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 197, // 49: openshell.v1.UpdateProviderRequest.credential_expires_at_ms:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry - 215, // 50: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider - 215, // 51: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 93, // 52: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile - 76, // 53: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride - 81, // 54: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh - 77, // 55: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant + 66, // 44: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning + 151, // 45: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate + 198, // 46: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry + 217, // 47: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 217, // 48: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 199, // 49: openshell.v1.UpdateProviderRequest.credential_expires_at_ms:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry + 217, // 50: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider + 217, // 51: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 95, // 52: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile + 78, // 53: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride + 83, // 54: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh + 79, // 55: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant 1, // 56: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 79, // 57: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial - 80, // 58: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput + 81, // 57: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial + 82, // 58: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput 1, // 59: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 212, // 60: openshell.v1.StoredProviderCredentialRefreshState.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 214, // 60: openshell.v1.StoredProviderCredentialRefreshState.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 1, // 61: openshell.v1.StoredProviderCredentialRefreshState.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 198, // 62: openshell.v1.StoredProviderCredentialRefreshState.material:type_name -> openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry - 199, // 63: openshell.v1.StoredProviderCredentialRefreshState.additional_output_keys:type_name -> openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry - 82, // 64: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 200, // 62: openshell.v1.StoredProviderCredentialRefreshState.material:type_name -> openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry + 201, // 63: openshell.v1.StoredProviderCredentialRefreshState.additional_output_keys:type_name -> openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry + 84, // 64: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus 1, // 65: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 200, // 66: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - 82, // 67: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 82, // 68: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 202, // 66: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + 84, // 67: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 84, // 68: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus 2, // 69: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory - 78, // 70: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential - 216, // 71: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 217, // 72: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 83, // 73: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery - 201, // 74: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry - 212, // 75: openshell.v1.StoredProviderProfile.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 93, // 76: openshell.v1.StoredProviderProfile.profile:type_name -> openshell.v1.ProviderProfile - 93, // 77: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile - 93, // 78: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 74, // 79: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 75, // 80: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 93, // 81: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 74, // 82: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem - 75, // 83: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 93, // 84: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile - 74, // 85: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 75, // 86: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 107, // 87: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding - 202, // 88: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - 203, // 89: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expires_at_ms:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry - 204, // 90: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - 205, // 91: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - 213, // 92: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 218, // 93: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue - 111, // 94: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation - 206, // 95: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry - 112, // 96: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule - 113, // 97: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint - 114, // 98: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule - 115, // 99: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules - 116, // 100: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules - 117, // 101: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary - 219, // 102: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 220, // 103: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 221, // 104: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule - 207, // 105: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry - 125, // 106: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision - 125, // 107: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision + 80, // 70: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential + 218, // 71: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint + 219, // 72: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary + 85, // 73: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery + 203, // 74: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry + 214, // 75: openshell.v1.StoredProviderProfile.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 95, // 76: openshell.v1.StoredProviderProfile.profile:type_name -> openshell.v1.ProviderProfile + 95, // 77: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile + 95, // 78: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile + 76, // 79: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem + 77, // 80: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 95, // 81: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile + 76, // 82: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem + 77, // 83: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 95, // 84: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile + 76, // 85: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem + 77, // 86: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 109, // 87: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding + 204, // 88: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry + 205, // 89: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expires_at_ms:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry + 206, // 90: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry + 207, // 91: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry + 215, // 92: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 220, // 93: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue + 113, // 94: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation + 208, // 95: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry + 114, // 96: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule + 115, // 97: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint + 116, // 98: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule + 117, // 99: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules + 118, // 100: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules + 119, // 101: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary + 221, // 102: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 222, // 103: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule + 223, // 104: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule + 209, // 105: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry + 127, // 106: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision + 127, // 107: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision 3, // 108: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus 3, // 109: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus - 213, // 110: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 208, // 111: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry - 63, // 112: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine - 63, // 113: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine - 132, // 114: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello - 135, // 115: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat - 142, // 116: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult - 143, // 117: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose - 133, // 118: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted - 134, // 119: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected - 136, // 120: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat - 137, // 121: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen - 143, // 122: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose - 138, // 123: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget - 139, // 124: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget - 140, // 125: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit - 144, // 126: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample - 146, // 127: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount - 219, // 128: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 145, // 129: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary - 148, // 130: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk - 147, // 131: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary - 148, // 132: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk - 219, // 133: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 167, // 134: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry - 213, // 135: openshell.v1.PolicyRevisionPayload.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 209, // 136: openshell.v1.PolicyRevisionPayload.provenance:type_name -> openshell.v1.PolicyRevisionPayload.ProvenanceEntry - 219, // 137: openshell.v1.DraftChunkPayload.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 210, // 138: openshell.v1.StoredPolicyRevision.provenance:type_name -> openshell.v1.StoredPolicyRevision.ProvenanceEntry - 211, // 139: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry - 222, // 140: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 222, // 141: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 222, // 142: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace - 212, // 143: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 215, // 110: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 210, // 111: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry + 65, // 112: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine + 65, // 113: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine + 134, // 114: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello + 137, // 115: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat + 144, // 116: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult + 145, // 117: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose + 135, // 118: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted + 136, // 119: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected + 138, // 120: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat + 139, // 121: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen + 145, // 122: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose + 140, // 123: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget + 141, // 124: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget + 142, // 125: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit + 146, // 126: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample + 148, // 127: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount + 221, // 128: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 147, // 129: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary + 150, // 130: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk + 149, // 131: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary + 150, // 132: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk + 221, // 133: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 169, // 134: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry + 215, // 135: openshell.v1.PolicyRevisionPayload.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 211, // 136: openshell.v1.PolicyRevisionPayload.provenance:type_name -> openshell.v1.PolicyRevisionPayload.ProvenanceEntry + 221, // 137: openshell.v1.DraftChunkPayload.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 212, // 138: openshell.v1.StoredPolicyRevision.provenance:type_name -> openshell.v1.StoredPolicyRevision.ProvenanceEntry + 213, // 139: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry + 224, // 140: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 224, // 141: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 224, // 142: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace + 214, // 143: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 5, // 144: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole 5, // 145: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole - 181, // 146: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember - 181, // 147: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember - 78, // 148: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 108, // 149: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding + 183, // 146: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember + 183, // 147: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember + 80, // 148: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential + 110, // 149: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding 10, // 150: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest 12, // 151: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest 14, // 152: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest @@ -14423,126 +14557,130 @@ var file_openshell_proto_depIdxs = []int32{ 30, // 157: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest 31, // 158: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest 32, // 159: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest - 39, // 160: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest - 41, // 161: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest - 42, // 162: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest - 43, // 163: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest - 45, // 164: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest - 49, // 165: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest - 51, // 166: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest - 57, // 167: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame - 58, // 168: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput - 65, // 169: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest - 66, // 170: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest - 67, // 171: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest - 72, // 172: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest - 73, // 173: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest - 97, // 174: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest - 99, // 175: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest - 101, // 176: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest - 68, // 177: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest - 85, // 178: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest - 87, // 179: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest - 89, // 180: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest - 91, // 181: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest - 69, // 182: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest - 104, // 183: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest - 223, // 184: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest - 224, // 185: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest - 110, // 186: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest - 119, // 187: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest - 121, // 188: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest - 123, // 189: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest - 106, // 190: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest - 126, // 191: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest - 127, // 192: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest - 130, // 193: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage - 141, // 194: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame - 61, // 195: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest - 150, // 196: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest - 152, // 197: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest - 154, // 198: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest - 156, // 199: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest - 158, // 200: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest - 160, // 201: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest - 162, // 202: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest - 164, // 203: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest - 166, // 204: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest - 6, // 205: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest - 8, // 206: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest - 173, // 207: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest - 175, // 208: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest - 177, // 209: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest - 179, // 210: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest - 182, // 211: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest - 184, // 212: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest - 186, // 213: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest - 11, // 214: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse - 13, // 215: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse - 15, // 216: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse - 33, // 217: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse - 33, // 218: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse - 34, // 219: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse - 35, // 220: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse - 36, // 221: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse - 37, // 222: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse - 38, // 223: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse - 40, // 224: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse - 48, // 225: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse - 48, // 226: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse - 44, // 227: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse - 46, // 228: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse - 50, // 229: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse - 55, // 230: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent - 57, // 231: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame - 55, // 232: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent - 70, // 233: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse - 70, // 234: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse - 71, // 235: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse - 96, // 236: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse - 95, // 237: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse - 98, // 238: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse - 100, // 239: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse - 102, // 240: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse - 70, // 241: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse - 86, // 242: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse - 88, // 243: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse - 90, // 244: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse - 92, // 245: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse - 103, // 246: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse - 105, // 247: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse - 225, // 248: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse - 226, // 249: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse - 118, // 250: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse - 120, // 251: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse - 122, // 252: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse - 124, // 253: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse - 109, // 254: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse - 129, // 255: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse - 128, // 256: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse - 131, // 257: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage - 141, // 258: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame - 62, // 259: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent - 151, // 260: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse - 153, // 261: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse - 155, // 262: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse - 157, // 263: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse - 159, // 264: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse - 161, // 265: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse - 163, // 266: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse - 165, // 267: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse - 168, // 268: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse - 7, // 269: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse - 9, // 270: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse - 174, // 271: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse - 176, // 272: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse - 178, // 273: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse - 180, // 274: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse - 183, // 275: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse - 185, // 276: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse - 187, // 277: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse - 214, // [214:278] is the sub-list for method output_type - 150, // [150:214] is the sub-list for method input_type + 33, // 160: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest + 34, // 161: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest + 41, // 162: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest + 43, // 163: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest + 44, // 164: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest + 45, // 165: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest + 47, // 166: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest + 51, // 167: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest + 53, // 168: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest + 59, // 169: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame + 60, // 170: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput + 67, // 171: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest + 68, // 172: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest + 69, // 173: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest + 74, // 174: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest + 75, // 175: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest + 99, // 176: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest + 101, // 177: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest + 103, // 178: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest + 70, // 179: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest + 87, // 180: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest + 89, // 181: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest + 91, // 182: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest + 93, // 183: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest + 71, // 184: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest + 106, // 185: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest + 225, // 186: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest + 226, // 187: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest + 112, // 188: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest + 121, // 189: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest + 123, // 190: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest + 125, // 191: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest + 108, // 192: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest + 128, // 193: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest + 129, // 194: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest + 132, // 195: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage + 143, // 196: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame + 63, // 197: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest + 152, // 198: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest + 154, // 199: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest + 156, // 200: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest + 158, // 201: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest + 160, // 202: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest + 162, // 203: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest + 164, // 204: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest + 166, // 205: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest + 168, // 206: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest + 6, // 207: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest + 8, // 208: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest + 175, // 209: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest + 177, // 210: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest + 179, // 211: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest + 181, // 212: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest + 184, // 213: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest + 186, // 214: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest + 188, // 215: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest + 11, // 216: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse + 13, // 217: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse + 15, // 218: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse + 35, // 219: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse + 35, // 220: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse + 36, // 221: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse + 37, // 222: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse + 38, // 223: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse + 39, // 224: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse + 40, // 225: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse + 35, // 226: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse + 35, // 227: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse + 42, // 228: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse + 50, // 229: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse + 50, // 230: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse + 46, // 231: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse + 48, // 232: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse + 52, // 233: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse + 57, // 234: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent + 59, // 235: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame + 57, // 236: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent + 72, // 237: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse + 72, // 238: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse + 73, // 239: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse + 98, // 240: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse + 97, // 241: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse + 100, // 242: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse + 102, // 243: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse + 104, // 244: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse + 72, // 245: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse + 88, // 246: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse + 90, // 247: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse + 92, // 248: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse + 94, // 249: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse + 105, // 250: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse + 107, // 251: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse + 227, // 252: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse + 228, // 253: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse + 120, // 254: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse + 122, // 255: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse + 124, // 256: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse + 126, // 257: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse + 111, // 258: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse + 131, // 259: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse + 130, // 260: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse + 133, // 261: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage + 143, // 262: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame + 64, // 263: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent + 153, // 264: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse + 155, // 265: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse + 157, // 266: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse + 159, // 267: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse + 161, // 268: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse + 163, // 269: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse + 165, // 270: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse + 167, // 271: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse + 170, // 272: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse + 7, // 273: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse + 9, // 274: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse + 176, // 275: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse + 178, // 276: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse + 180, // 277: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse + 182, // 278: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse + 185, // 279: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse + 187, // 280: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse + 189, // 281: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse + 216, // [216:282] is the sub-list for method output_type + 150, // [150:216] is the sub-list for method input_type 150, // [150:150] is the sub-list for extension type_name 150, // [150:150] is the sub-list for extension extendee 0, // [0:150] is the sub-list for field type_name @@ -14555,33 +14693,33 @@ func file_openshell_proto_init() { } file_openshell_proto_msgTypes[15].OneofWrappers = []any{} file_openshell_proto_msgTypes[16].OneofWrappers = []any{} - file_openshell_proto_msgTypes[49].OneofWrappers = []any{ + file_openshell_proto_msgTypes[51].OneofWrappers = []any{ (*ExecSandboxEvent_Stdout)(nil), (*ExecSandboxEvent_Stderr)(nil), (*ExecSandboxEvent_Exit)(nil), } - file_openshell_proto_msgTypes[50].OneofWrappers = []any{ + file_openshell_proto_msgTypes[52].OneofWrappers = []any{ (*TcpForwardInit_Ssh)(nil), (*TcpForwardInit_Tcp)(nil), } - file_openshell_proto_msgTypes[51].OneofWrappers = []any{ + file_openshell_proto_msgTypes[53].OneofWrappers = []any{ (*TcpForwardFrame_Init)(nil), (*TcpForwardFrame_Data)(nil), } - file_openshell_proto_msgTypes[52].OneofWrappers = []any{ + file_openshell_proto_msgTypes[54].OneofWrappers = []any{ (*ExecSandboxInput_Start)(nil), (*ExecSandboxInput_Stdin)(nil), (*ExecSandboxInput_Resize)(nil), } - file_openshell_proto_msgTypes[56].OneofWrappers = []any{ + file_openshell_proto_msgTypes[58].OneofWrappers = []any{ (*SandboxStreamEvent_Sandbox)(nil), (*SandboxStreamEvent_Log)(nil), (*SandboxStreamEvent_Event)(nil), (*SandboxStreamEvent_Warning)(nil), (*SandboxStreamEvent_DraftPolicyUpdate)(nil), } - file_openshell_proto_msgTypes[81].OneofWrappers = []any{} - file_openshell_proto_msgTypes[105].OneofWrappers = []any{ + file_openshell_proto_msgTypes[83].OneofWrappers = []any{} + file_openshell_proto_msgTypes[107].OneofWrappers = []any{ (*PolicyMergeOperation_AddRule)(nil), (*PolicyMergeOperation_RemoveEndpoint)(nil), (*PolicyMergeOperation_RemoveRule)(nil), @@ -14589,36 +14727,36 @@ func file_openshell_proto_init() { (*PolicyMergeOperation_AddAllowRules)(nil), (*PolicyMergeOperation_RemoveBinary)(nil), } - file_openshell_proto_msgTypes[124].OneofWrappers = []any{ + file_openshell_proto_msgTypes[126].OneofWrappers = []any{ (*SupervisorMessage_Hello)(nil), (*SupervisorMessage_Heartbeat)(nil), (*SupervisorMessage_RelayOpenResult)(nil), (*SupervisorMessage_RelayClose)(nil), } - file_openshell_proto_msgTypes[125].OneofWrappers = []any{ + file_openshell_proto_msgTypes[127].OneofWrappers = []any{ (*GatewayMessage_SessionAccepted)(nil), (*GatewayMessage_SessionRejected)(nil), (*GatewayMessage_Heartbeat)(nil), (*GatewayMessage_RelayOpen)(nil), (*GatewayMessage_RelayClose)(nil), } - file_openshell_proto_msgTypes[131].OneofWrappers = []any{ + file_openshell_proto_msgTypes[133].OneofWrappers = []any{ (*RelayOpen_Ssh)(nil), (*RelayOpen_Tcp)(nil), } - file_openshell_proto_msgTypes[135].OneofWrappers = []any{ + file_openshell_proto_msgTypes[137].OneofWrappers = []any{ (*RelayFrame_Init)(nil), (*RelayFrame_Data)(nil), } - file_openshell_proto_msgTypes[165].OneofWrappers = []any{} - file_openshell_proto_msgTypes[166].OneofWrappers = []any{} + file_openshell_proto_msgTypes[167].OneofWrappers = []any{} + file_openshell_proto_msgTypes[168].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: 6, - NumMessages: 206, + NumMessages: 208, NumExtensions: 0, NumServices: 1, }, diff --git a/sdk/go/proto/openshellv1/openshell_grpc.pb.go b/sdk/go/proto/openshellv1/openshell_grpc.pb.go index 40d625a394..92c94ef299 100644 --- a/sdk/go/proto/openshellv1/openshell_grpc.pb.go +++ b/sdk/go/proto/openshellv1/openshell_grpc.pb.go @@ -33,6 +33,8 @@ const ( OpenShell_AttachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/AttachSandboxProvider" OpenShell_DetachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/DetachSandboxProvider" OpenShell_DeleteSandbox_FullMethodName = "/openshell.v1.OpenShell/DeleteSandbox" + OpenShell_StopSandbox_FullMethodName = "/openshell.v1.OpenShell/StopSandbox" + OpenShell_StartSandbox_FullMethodName = "/openshell.v1.OpenShell/StartSandbox" OpenShell_CreateSshSession_FullMethodName = "/openshell.v1.OpenShell/CreateSshSession" OpenShell_ExposeService_FullMethodName = "/openshell.v1.OpenShell/ExposeService" OpenShell_GetService_FullMethodName = "/openshell.v1.OpenShell/GetService" @@ -122,6 +124,10 @@ type OpenShellClient interface { DetachSandboxProvider(ctx context.Context, in *DetachSandboxProviderRequest, opts ...grpc.CallOption) (*DetachSandboxProviderResponse, 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) // 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. @@ -379,6 +385,26 @@ func (c *openShellClient) DeleteSandbox(ctx context.Context, in *DeleteSandboxRe 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) CreateSshSession(ctx context.Context, in *CreateSshSessionRequest, opts ...grpc.CallOption) (*CreateSshSessionResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateSshSessionResponse) @@ -985,6 +1011,10 @@ type OpenShellServer interface { DetachSandboxProvider(context.Context, *DetachSandboxProviderRequest) (*DetachSandboxProviderResponse, 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) // 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. @@ -1172,6 +1202,12 @@ func (UnimplementedOpenShellServer) DetachSandboxProvider(context.Context, *Deta 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) CreateSshSession(context.Context, *CreateSshSessionRequest) (*CreateSshSessionResponse, error) { return nil, status.Error(codes.Unimplemented, "method CreateSshSession not implemented") } @@ -1535,6 +1571,42 @@ func _OpenShell_DeleteSandbox_Handler(srv interface{}, ctx context.Context, dec 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_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 { @@ -2485,6 +2557,14 @@ var OpenShell_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteSandbox", Handler: _OpenShell_DeleteSandbox_Handler, }, + { + MethodName: "StopSandbox", + Handler: _OpenShell_StopSandbox_Handler, + }, + { + MethodName: "StartSandbox", + Handler: _OpenShell_StartSandbox_Handler, + }, { MethodName: "CreateSshSession", Handler: _OpenShell_CreateSshSession_Handler,