Refactor the supervisor so the Kubernetes sidecar topology is driven by a
first-class RFC 0012 isolation backend instead of an inline conditional
branch. The agent container's supervisor now resolves a `sidecar` backend
from the driver-provided `TopologyDescriptor` and runs the standard
`attach -> confirm -> start_agent` lifecycle, identically to the in-pod
backend; the supervisor no longer branches on which backend it resolved.
The agent container stays autonomous and spawns its own workload, so the
backend wraps the existing agent/process path rather than inverting control.
Network mediation continues to run in the separate network-sidecar container
(framed as the RFC's network-mediation component), which hosts no local
mediation ingress in the backend.
Fail-closed hardening:
- `attach` validates the descriptor payload at the trust boundary (empty for
this topology), mirroring the in-pod backend.
- `confirm` peeks the authoritative control link to the network container and
refuses to advance if it is already closed, so the workload is never
launched only to be torn down on the first `wait`.
- The supervisor rejects a single process that enables both --mode=network
and --mode=process under sidecar enforcement (never driver-produced) instead
of silently holding open without spawning the workload.
- `wait` races the workload against the control-closed signal and folds any
wait error to a closed exit, dropping the workload handle to reap it.
Add `SIDECAR_BACKEND_ID` and `TopologyDescriptor::sidecar()` to the contract
crate, wire the driver to set the sidecar descriptor on the agent container,
and update the sandbox architecture doc. Also fix pre-existing clippy lints in
openshell-isolation surfaced by the workspace `-D warnings` gate.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
Implement the Isolation Backend interface defined by RFC 0012 and migrate
the in-pod logic that was coupled to the supervisor behind an in-pod backend.
Squashed from the RFC 0012 implementation series (POC + subsequent RFC
alignment revisions).
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
Keep RFC 0012 focused on placing binary-aware enforcement behind the Isolation Backend contract instead of requiring a new connection-initiation capture mechanism. Define identity as trusted resolution for an accepted connection before policy evaluation, retain fail-closed attribution requirements, and document the current authorization-time procfs behavior. This preserves RFC 0002's binary gating baseline while allowing future backends to provide stronger identity resolution without changing the contract.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
All line numbers and function names verified against the post-sidecar
state of the codebase (commit 8eacb477, #2076).
Changes:
- process.rs: ProcessHandle::spawn 440→527, netns param 446→535;
drop_privileges call sites 603/700→710/812, enforcement 613/705→721/818-819;
enter_netns_and_sandbox now documented at ssh.rs:1245
- CLONE_NEWNET call sites: process.rs:589→695, ssh.rs:619/1186→653/1262,
supervisor_session.rs:610→735, netns/mod.rs:363→342 (226 unchanged)
- CLONE_NEWNS: was one unshare at :393; now unshare at :449 and a new
setns at :480 added for sidecar mount-namespace entry
- nft fail-open: line 264→265, return Ok(()) range 272-277→277; note
that the sidecar path (netns/mod.rs:477) requires nft and returns an
error if absent, fixing the invariant bug for the sidecar topology
- nft_ruleset.rs: policy accept 41→53; accept rules 43-49→56-92;
reject rules now at 106+
- VM driver MASQUERADE: runtime.rs:417/436→418/437
- Agent command: main.rs:331→601; sleep infinity driver.rs:1886→2937,
clarify it is set via SANDBOX_COMMAND env var
- OPA evaluation: proxy.rs:1611 / evaluate_opa_tcp renamed to
authorize_egress_intent at proxy.rs:1955; NetworkInput built at :2032
- openshell.proto: clarify no lifecycle Attach; note AttachSandboxProvider
(provider record attachment, not isolation lifecycle)
- README.md appendix: update pinned commit reference a5161d0→8eacb477
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
- Repin codebase-grounding.md to 8eacb477 (sidecar supervisor topology,
#2076); update capabilities line numbers (1534→2538, 1540→2544), init
container line numbers (191→423, 993→1506, 1185→2113), and remove the
stale "no native sidecars today" claim. Add openshell-network-init and
openshell-supervisor-network sidecar entries and expand the rg pattern.
- Add Implementation column to topology-matrix.md; mark Co-located/in-pod
and Same-pod composite as implemented (original topology and #2076
respectively); remaining patterns noted as proposed.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
- Introduced topology nomenclature to align with how we've been talking about this in other conversations
- Simplified the explanation in the proposal section
- Clarified binary identity
- Simplified the initial contract to require the minimal interface necessary to satisfy all known topologies
- Confirmed this will work with the proposed mxc (RFC 0013) proposal
Contract completion: add MediationIngress/MediatedConnection so the proxy
receives connections placement-neutrally; complete the runtime types
(BoundaryExitStatus/BoundarySignal, BoundaryTerminal::resize, IdentitySource
Send+Sync) and a machine-readable BackendErrorKind; name the AdmittedBoundary-
Requirements admission block retained by VerifiedBoundaryDescriptor; show
claim_id/generation in ClaimContext; require a canonical bounded descriptor
encoding; define capability comparison. Mark MediationIngress, the admission
block, and claim generation as contract additions the in-pod POC does not yet
implement.
Security semantics: make confirmation mandatory and lifetime-long, but correct
the enforcement model: Landlock/seccomp are monotonic while netns/nftables are
mutable, so require the mutation authority fenced from the workload, atomic
default-deny updates, and fail-closed on lease loss rather than calling the
boundary irreversible. "Confirms effective enforcement", not "reads rules
back". Bind Attested identity to sandbox and claim id/generation. Tighten the
execution-domain invariant (admission-visible named capability + audit, whole
descendant trees). Distinguish structural pre-Running ordering from dynamic
post-termination rejection; note adoption must remove legacy bypass paths.
Lifecycle: drop the "establishes before supervisor boot" contradiction; add
shutdown to the supervisor sequence; rewrite the in-pod migration appendix
(policy-free attach, netns/rules at claim/bind, agent().wait(), wait_terminated,
idempotent shutdown). The RFC is implemented only after the in-pod backend
passes its release gates.
Topology: narrow node-enforcer claims to "network-setup capabilities off the
pod"; nuance NetworkPolicy (coarse L3/L4 defense in depth, not a proxy
replacement); drop the proxy-policy-reprograms-boundary claim; label the
containment table a kernel-compromise ceiling.
Grounding: re-pin to the RFC's parent a5161d0 (process.rs and proxy.rs line
numbers refreshed; others unchanged and reverified).
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
Tighten RFC 0012 for review without changing its structure or tone:
- Remove the undefined `BoundaryIntent` from `attach`, and state that `claim`
is the only transition that binds sandbox identity, policy, agent, and
resources; an attached boundary holds no claimed or untrusted workload.
- Add the runtime interface definitions (`BoundaryExec`/`ExecSession`,
`BoundaryPortForward`, `EventSource`) and their semantics: owned distinct
stdio, PTY resize, placement-neutral exit and signals, stable repeated
`wait`, validated loopback targets, and single-consumer events.
- Make boundary confirmation mandatory (`MUST`) and lifetime-long, and state
retry, termination, and cleanup semantics (only attach is auto-retryable; a
lost `start_agent` returns the existing process; descendants are owned;
`wait_terminated` and an idempotent `shutdown`).
- Require trusted admission to supply the expected backend id, contract
version, policy digest, and capabilities that a backend cannot lower; add the
no-silent-weakening policy invariant; mark `VerifiedBoundaryDescriptor`
privately constructed and distinguish envelope from contract version; make
`ResourceBinding` driver-issued, bound, and non-wideable.
- Make the rollout executable and forbid a silent default backend.
topology-matrix: rename "sidecar proxy" to sidecar-assisted (the proxy stays
with the supervisor), frame sidecar and node as composite backends, move the
single-pod outer sandbox to the shared-kernel cell, and note a node enforcer
alone is not `restricted`-compatible.
codebase-grounding: re-pin anchors to the RFC's parent ba21bb32 (driver.rs and
proxy.rs line numbers refreshed) and add a permalink base; correct the event
wording so a denial carries `Evidence` plus request-specific L7 data.
Set state: review and link PR #2048.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
Introduces RFC 0012, the runtime-selectable Isolation Backend contract: a
pluggable component that establishes and enforces an agent's isolation
boundary across network, filesystem, syscall, and identity, while the
supervisor stays the policy authority (proxy, policy, audit) and the agent's
only egress.
Includes the supporting topology matrix and codebase-grounding notes.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
Run the constructor gateway-discovery error test only on Linux, matching the
production branch that inspects the Podman bridge gateway. macOS uses its
Podman machine callback path and correctly skips that inspection.
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Establish the Phase 1 reference implementation proposed by RFC 0012
while retaining the existing Cargo and Mise workflows during evaluation.
- pin Bazel 9.1.1 and configure Bzlmod, rules_rs, LLVM, protobuf, and
Rust 1.95 toolchains
- import third-party crates from Cargo metadata and propagate the
workspace version into Bazel targets
- add library, binary, proc-macro, unit-test, and integration-test
targets across the supported Rust workspace crates and drivers
- generate protobuf Rust sources and descriptor sets under Bazel while
preserving Cargo-compatible generated-code imports
- annotate aws-lc-sys and zstd-sys native dependencies, build Z3 4.15.2
from source, and generate z3-sys bindings
- make CLI and procfs test fixtures available as explicit Bazel inputs
without relying on fixed host binary paths
- define optimized release targets for Linux x86_64 and aarch64 CLI,
sandbox, and gateway binaries, plus macOS aarch64 artifacts
- add Bazel, buildifier, and lcov to the Nix development environment
RFC: 0012 (rfc12 branch)
Refs: #2491
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
The log prefix value passed to nft contains colons (e.g.
openshell:bypass:sandbox-cc817378:) but was not wrapped in double
quotes. Since nft concatenates argv entries and parses the result,
the bare colons cause a syntax error that silently prevents all
bypass-attempt LOG rules from installing.
Wrap log prefix values in nft-quoted strings via a new nft_quote()
helper that strips embedded double-quotes (nft quoted strings don't
support escape sequences). All four log-rule generation sites (TCP
and UDP, for both per-sandbox and sidecar rulesets) are updated.
Fixes#2470
Signed-off-by: Grace Smith <grasmith@redhat.com>
* test(server): stabilize watch span cancellation test
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* test(server): poll span exporter at fixed interval
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
---------
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(podman): combine sandbox stop and removal
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(podman): use libpod volume removal parameter
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(podman): preserve removal timeout margin
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* test(podman): use virtual time for removal delay
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
---------
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Give each span-assertion test its own thread-scoped in-memory exporter so
parallel tests cannot contaminate or reset captured spans. Keep a bare global
tracing registry only to preserve callsite interest, and serialize scoped
subscriber changes because tracing caches that interest process-wide.
Remove the test-only OTLP collector, polling delivery barrier,
transport-specific test, and direct opentelemetry-proto dependency. Seed the
expected persistence conflict before tracing begins so its assertion window
contains only the operation under test.
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Extract common OpenTelemetry provider construction into openshell-otel so
OpenShell services share one OTLP/gRPC export implementation.
The shared crate owns:
- typed exporter setup errors and non-fatal provider enablement;
- endpoint trimming and URI validation before lazy exporter connection;
- fixed and environment-or-default service-name policies;
- service version and caller-supplied resource attributes;
- batch tracer-provider construction; and
- span-only tracing layers that exclude OpenTelemetry exporter callsites.
Migrate the gateway to the shared provider while retaining its configurable
service name, error marking, and tracing test collector. Add the shared crate
to the architecture inventory and document the tracing boundary.
Refs #2507
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Add a published Issue Triage and Lifecycle page and align AGENTS.md,
CONTRIBUTING.md, README.md, the PR template, and the issue-handling
skills on the state:*/agent:* label model.
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Add an opt-in OTLP/gRPC trace exporter to the gateway. Export is enabled
by the presence of an `[openshell.gateway.otlp]` table with an endpoint;
there is no separate toggle.
Instrumented:
- Inbound request server spans, named for the RPC (`$service/$method`) or
`{method} {path}` for plain HTTP. They continue valid W3C `traceparent`
context when present and start a new trace otherwise. gRPC spans also
carry `rpc.system`, `rpc.service`, `rpc.method`, and trailer-derived
`rpc.grpc.status_code`.
- Compute driver calls (create, delete, list, get, validate, watch) as
client spans anchored on the `ComputeDriver` contract.
- Store reads and writes as children of the current request or loop span.
- Work with no inbound request: compute driver initialization, the sandbox
reconcile sweep, provider credential refresh tick, and driver watch events.
Each roots one operation trace so its child work does not arrive as anonymous
single-span traces.
This is deliberately not exhaustive. Auth, policy evaluation, and
middleware remain uninstrumented, as do store lifecycle calls (`ping`,
`close`) that a readiness poll would turn into a span per tick. The aim is
a useful trace tree at a reviewable size; coverage can grow against real
traces.
Design notes:
- The TOML table owns whether and where to export. The SDK `OTEL_*`
variables own how; sampling, batching, and limits are not mirrored into
gateway config.
- The OpenTelemetry layer exports spans only. Existing `tracing` events
remain on the stdout and sandbox-log paths and are not copied into trace
payloads.
- Telemetry never blocks the gateway. A malformed endpoint logs an error
and disables export rather than failing startup, and buffered spans are
drained during graceful shutdown.
- Failed spans carry error status without a separate `error.type` attribute.
Request spans use HTTP status and gRPC response trailers; driver spans use
the returned gRPC status; autonomous loop spans record failed results
explicitly. Store spans exempt `UniqueViolation` and `Conflict`, because
those errors report expected contention such as a held lease or an
optimistic-concurrency retry.
- The compute driver is reachable only through `TracedDriver::call`, so a
call cannot skip its span. This is the client half of a client/server pair
and the single place to inject context if drivers move out of process.
- Tests share one process-wide subscriber and in-memory exporter because
`tracing` caches callsite interest globally.
Inbound W3C trace context is propagated into gateway request spans. Context
is not yet injected into outbound driver calls, so a future out-of-process
driver would still need propagation at the `TracedDriver` seam.
The Helm chart is intentionally unchanged, so OTLP export cannot yet be
enabled on a chart-deployed gateway.
Refs #2507
Signed-off-by: Kris Hicks <khicks@nvidia.com>
* feat(auth): implement RFC 0011 Phase 2 workspace authorization
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): address PR review feedback on workspace authorization
- Docker e2e: add --health-port and switch readiness probe from
`openshell status` to `curl /healthz`, fixing a false-positive
readiness check in OIDC mode where the CLI exited 0 without
actually contacting the gateway
- ListWorkspaces: move membership filtering from post-query N+1
lookups into a SQL EXISTS subquery so pagination applies to the
visible set, not the global ordering. Add generic
list_with_membership to the persistence layer.
- Descriptor validator: reject role/scope fields on unauthenticated
and sandbox auth modes, and allow-list workspace_role as
user/admin and global_role as platform_admin to catch typos at
startup
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(server): use authed request in delete telemetry test
The workspace authorization added by the Phase 2 auth changes requires
a Principal on every delete request. The delete-telemetry test was still
using a bare Request::new, so extract_principal failed before the handler
could acquire the delete gate, causing a 5-second timeout flake.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): address gator review findings for workspace authorization
- Inject unauthenticated-local-dev principal in no-auth gateway mode so
handlers that call extract_principal() always find one.
- Cap label-selector membership query at MAX_PAGE_SIZE instead of
u32::MAX to bound the in-memory read.
- Authorize workspace membership before resolving workspace existence in
all sandbox RPCs to prevent workspace-name enumeration by non-members.
- Remove dead_code allow on AuthorizedWorkspace.workspace now that
callers use the normalized name from the authz result.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): close workspace-name oracle and label-selector truncation
Swap authorize-before-resolve ordering in 27 handlers across
provider.rs, service.rs, policy.rs, and workspace.rs to prevent
CWE-203 workspace-name enumeration by non-members.
Add combined membership+label SQL query (list_with_membership_and_selector)
to both persistence backends so ListWorkspaces with label selectors no
longer silently drops results beyond the first page of membership matches.
Signed-off-by: Derek Carr <decarr@redhat.com>
* test(auth): add non-member rejection and membership+label persistence tests
Add comprehensive test coverage for workspace authorization changes:
- Non-member rejection tests across all 44 workspace-scoped handlers
(sandbox, provider, service, policy, workspace, inference) verifying
PERMISSION_DENIED is returned instead of NOT_FOUND to prevent
CWE-203 workspace-name oracle
- Persistence test for list_with_membership_and_selector verifying
SQL-level membership EXISTS + label filtering, multiple predicates,
no-match cases, and pagination
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): format merged import line in sandbox tests
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): address gator re-review findings on workspace authorization
- Fix TUI unconditionally setting providers_v2_enabled after provider
refresh; read the actual gateway setting via GetGatewayConfig at
startup instead
- Fix SQLite json_extract with dotted label keys (e.g. example.com/env)
by quoting the key in the JSON path
- Add authed_request wrappers to upstream OCI identity tests that were
missing a principal after rebase
- Add test proving GetGatewayConfig is accessible without Platform Admin
- Add test for dotted/prefixed Kubernetes-style label key filtering
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): address second gator re-review findings
- Loosen GetGatewayConfig from platform_admin to scope-only so workspace
users can discover providers_v2_enabled during sandbox creation with
inferred-provider commands; update proto descriptor, descriptor
validation, and RFC 0011 access table
- Add validate_label_selector to handle_list_workspaces and escape
single quotes in SQLite json_extract interpolation (CWE-89
defense-in-depth)
- Re-fetch providers_v2_enabled after TUI gateway switch so the new
gateway's capability is reflected
- Add e2e test for workspace user with inferred-provider command
- Add persistence test for adversarial label keys with SQL injection
attempts
- Add handler test for invalid label selector rejection in
ListWorkspaces
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): address third gator review findings
- Cap label selector pairs at 64 (CWE-400) to bound SQLite dynamic SQL
- Add SCOPE_ONLY_METHODS allowlist for scope-without-role RPCs (CWE-863)
- Normalize ID-based data-plane handlers to return NOT_FOUND for
unauthorized sandboxes, closing the cross-workspace oracle (CWE-203)
- Fix TUI provider profile cache lookup key mismatch for legacy
providers with empty profile_workspace
- Add whoami to CLI skill reference command tree
- Update TUI skill doc with workspace, provider, and settings coverage
- Document scope/workspace orthogonality on GetGatewayConfig proto
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): extend CWE-203 normalization to policy.rs sandbox handlers
GetSandboxConfig and GetSandboxLogs in policy.rs had the same
fetch-before-authorize pattern that leaked cross-workspace sandbox
existence. Promote fetch_and_authorize_sandbox to pub(super) and
use it from both sandbox.rs and policy.rs handlers.
Signed-off-by: Derek Carr <decarr@redhat.com>
* test(auth): update assertions for CWE-203 sandbox ID normalization
Cross-workspace sandbox access via ID-based handlers now returns
NOT_FOUND instead of PERMISSION_DENIED to prevent existence inference.
Update the unit test and OIDC e2e assertion to match.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(auth): narrow CWE-203 error mapping and correct whoami output formats
Only remap PERMISSION_DENIED to NOT_FOUND in fetch_and_authorize_sandbox
and RevokeSshSession, letting INTERNAL and UNAUTHENTICATED propagate
as-is. Fix whoami --output format values in cli-reference.md to match
the actual CLI (table/json/yaml, not text/json).
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(ci): share network namespace with Keycloak in containerized CI
In GitHub Actions job containers, Docker port publishing lands on the
host, not inside the job container. Detect this environment and attach
Keycloak to the job container's network namespace instead, with
hardened defaults (cap-drop ALL, no-new-privileges, loopback-only
listener).
Signed-off-by: Derek Carr <decarr@redhat.com>
---------
Signed-off-by: Derek Carr <decarr@redhat.com>
ForwardSpec accepts IPv6 bind addresses, but ssh_forward_arg() emitted
them unbracketed (e.g. ::1:8080:127.0.0.1:8080), which OpenSSH rejects
as a bad local forwarding specification. access_url() likewise produced
invalid URLs like http://::1:8080/.
Extract a shared bracket_ipv6_host() helper and use it in
ssh_forward_arg(), access_url() (via format_gateway_url), and
format_gateway_url() so IPv6 literals are bracketed consistently.
Fixes#2279
Signed-off-by: Russell Bryant <rbryant@redhat.com>
The subprocess-based integration tests inherit the full parent
environment. If the developer has OPENSHELL_GATEWAY_INSECURE=true
set in their shell, it leaks into the spawned CLI process and causes
it to connect with .with_no_client_auth(), skipping the mTLS client
certificate. The test server requires mTLS, so it responds with
CertificateRequired and the test fails.
Strip OPENSHELL_GATEWAY_INSECURE, OPENSHELL_GATEWAY,
OPENSHELL_GATEWAY_ENDPOINT, and OPENSHELL_WORKSPACE from the
subprocess environment. The test already sets --gateway and
--gateway-endpoint explicitly via CLI args, so these env vars
should not influence the subprocess behavior.
Reported-by: Seth Jennings
Signed-off-by: Roland Huß <rhuss@redhat.com>
* refactor(compute): extract create_sandbox_record and update_sandbox_record helpers
Split apply_sandbox_update_locked into named helpers to make the two
distinct paths explicit: create_sandbox_record for first-observation events
and update_sandbox_record for subsequent driver snapshots on existing sandboxes.
The dispatcher now uses a match on the existing record rather than an
early-return guard.
No behavior change.
Signed-off-by: Evan Lezar <elezar@nvidia.com>
* refactor(compute): make sandbox readiness gateway-owned across all drivers
Introduce compute_phase_components and apply_readiness_conditions to
centralise the gateway's phase composition logic. The public SandboxPhase
is now determined by combining the backend phase reported by the driver with
supervisor session presence, independent of the driver implementation.
Remove SupervisorReadiness from the driver contract. Running containers
always report BackendReady; the gateway owns the Ready decision. Rename
the dispatcher match to three arms so that status-less events for existing
sandboxes are a documented no-op rather than a silent pass-through.
Drop backend_ready_no_session and the SupervisorNotConnected condition.
The BackendReady driver condition plus the Provisioning phase already
communicates that the backend is up but the supervisor has not connected.
The redundant condition added noise without new information.
Closes#1951
Signed-off-by: Evan Lezar <elezar@nvidia.com>
* fix(compute): expose disconnected supervisor readiness
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
* docs(sandboxes): clarify supervisor readiness lifecycle
Signed-off-by: Evan Lezar <elezar@nvidia.com>
---------
Signed-off-by: Evan Lezar <elezar@nvidia.com>
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
Co-authored-by: Drew Newberry <anewberry@nvidia.com>
parse_duration_to_ms was moved to commands/common.rs since the
original PR was opened, but it still split the last byte of the
input with split_at(s.len() - 1), which panics when the final
character is multi-byte UTF-8 (e.g. 'openshell logs my-sandbox
--since 5€').
Split off the last character using its UTF-8 length instead, so
invalid units surface the intended 'unknown duration unit' error.
Add regression tests in commands/common.rs.
Signed-off-by: Andrew White <andrewh@cdw.com>
The Kubernetes driver's default workspace PVC never set storageClassName,
so on clusters with no default StorageClass the PVC stayed Pending and
sandbox creation failed.
Add a workspace_storage_class option to KubernetesComputeConfig, wired
through SandboxPodParams into the generated volumeClaimTemplates. When
non-empty it sets storageClassName; empty preserves the current behavior
of relying on the cluster default StorageClass.
Expose it via the OPENSHELL_K8S_WORKSPACE_STORAGE_CLASS env var on both
the standalone driver and the embedded gateway runtime defaults, and via
the server.workspaceStorageClass Helm value.
Closes#2442
Signed-off-by: lr90 <qiuweimin@matrixorigin.cn>
Replace yield_now() spin-loop with sleep(10ms) polling in
delete_handler_ends_telemetry_for_the_resolved_sandbox_id. The
single-threaded tokio runtime starves the spawn_blocking threads
used by SQLite when yield_now() burns 100% CPU waiting for the
delete gate entry count. Increase the timeout from 1s to 5s for
consistency with similar guard tests.
Signed-off-by: Derek Carr <decarr@redhat.com>