Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/e2e-docker-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ on:
required: false
type: string
default: >-
[{"suite":"python","cmd":"mise run --no-deps --skip-deps e2e:python","apt_packages":"","python_proto":true,"mcp":false},{"suite":"oidc-python","cmd":"mise run --no-deps --skip-deps e2e:oidc-python:docker","apt_packages":"","python_proto":true,"mcp":false},{"suite":"oidc-pkce-docker","cmd":"mise run --no-deps --skip-deps e2e:oidc-pkce:docker","apt_packages":"openssh-client","python_proto":false,"mcp":false},{"suite":"rust-docker","cmd":"mise run --no-deps --skip-deps e2e:rust","apt_packages":"openssh-client","python_proto":false,"mcp":false},{"suite":"mcp","cmd":"mise run --no-deps --skip-deps e2e:mcp","apt_packages":"","python_proto":false,"mcp":true}]
[{"suite":"python","cmd":"mise run --no-deps --skip-deps e2e:python","apt_packages":"","python_proto":true,"mcp":false},{"suite":"go","cmd":"mise run --no-deps --skip-deps e2e:go:docker","apt_packages":"","python_proto":false,"mcp":false},{"suite":"oidc-python","cmd":"mise run --no-deps --skip-deps e2e:oidc-python:docker","apt_packages":"","python_proto":true,"mcp":false},{"suite":"oidc-pkce-docker","cmd":"mise run --no-deps --skip-deps e2e:oidc-pkce:docker","apt_packages":"openssh-client","python_proto":false,"mcp":false},{"suite":"rust-docker","cmd":"mise run --no-deps --skip-deps e2e:rust","apt_packages":"openssh-client","python_proto":false,"mcp":false},{"suite":"mcp","cmd":"mise run --no-deps --skip-deps e2e:mcp","apt_packages":"","python_proto":false,"mcp":true}]

permissions:
actions: read
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ ocsf_emit!(event);
- Domain types in `sdk/go/openshell/v1/types/` must not import proto packages.
- Converters in `sdk/go/openshell/v1/internal/converter/` deep-copy slices and maps at boundaries.
- Tests use bufconn for in-process gRPC and testify for assertions.
- E2E tests live in `e2e/go/` (a separate Go module using a `replace` directive to `sdk/go/`, gated by the `e2e` build tag) and connect to a real gateway via `gateway.NewClient`. Run `mise run e2e:go` with Podman or `mise run e2e:go:docker` with Docker.

## TypeScript SDK (`sdk/typescript/`)

Expand Down
24 changes: 24 additions & 0 deletions e2e/go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module github.com/NVIDIA/OpenShell/e2e/go

go 1.25.13

require (
github.com/NVIDIA/OpenShell/sdk/go v0.0.0-00010101000000-000000000000
github.com/stretchr/testify v1.11.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/grpc v1.83.2 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/NVIDIA/OpenShell/sdk/go => ../../sdk/go
52 changes: 52 additions & 0 deletions e2e/go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
279 changes: 279 additions & 0 deletions e2e/go/harness_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//go:build e2e

// Package e2e contains end-to-end tests for the Go SDK, run against a real
// OpenShell gateway with `mise run e2e:go` (Podman) or
// `mise run e2e:go:docker` (Docker).
package e2e

import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"time"

"github.com/stretchr/testify/require"

v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1"
"github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway"
"github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types"
)

var (
sharedClient *v1.Client
gatewayName string
)

func TestMain(m *testing.M) {
gatewayName = os.Getenv("OPENSHELL_GATEWAY")
if gatewayName == "" {
// No gateway configured; requireClient skips every test individually.
os.Exit(m.Run())
}

cfg, err := gateway.LoadConfig(gatewayName)
if err != nil {
fmt.Fprintf(os.Stderr, "e2e: failed to load gateway config: %v\n", err)
os.Exit(1)
}

var opts []gateway.ClientOption
if opt := mtlsClientOption(cfg.Dir); opt != nil {
opts = append(opts, opt)
}

client, err := gateway.NewClient(gatewayName, opts...)
if err != nil {
fmt.Fprintf(os.Stderr, "e2e: failed to build gateway client: %v\n", err)
os.Exit(1)
}
sharedClient = client

if err := waitForPersistenceReady(sharedClient); err != nil {
fmt.Fprintf(os.Stderr, "e2e: %v\n", err)
_ = sharedClient.Close()
os.Exit(1)
}

code := m.Run()
_ = sharedClient.Close()
os.Exit(code)
}

// mtlsClientOption inspects <dir>/mtls for an on-disk certificate bundle
// written by e2e_register_mtls_gateway (see e2e/support/gateway-common.sh)
// and returns a ClientOption applying it. Returns nil when no bundle is
// present (e.g. plaintext or OIDC e2e lanes), so callers should skip it.
func mtlsClientOption(dir string) gateway.ClientOption {
mtlsDir := filepath.Join(dir, "mtls")
caPath := filepath.Join(mtlsDir, "ca.crt")
if _, err := os.Stat(caPath); err != nil {
return nil
}

cfg := &types.TLSConfig{CAFile: caPath}

certPath := filepath.Join(mtlsDir, "tls.crt")
keyPath := filepath.Join(mtlsDir, "tls.key")
if _, err := os.Stat(certPath); err == nil {
if _, err := os.Stat(keyPath); err == nil {
cfg.CertFile = certPath
cfg.KeyFile = keyPath
}
}

return gateway.WithTLS(cfg)
}

// requireClient returns the shared gateway client, skipping the calling test
// when OPENSHELL_GATEWAY was not set at process startup.
func requireClient(t *testing.T) *v1.Client {
t.Helper()
if gatewayName == "" {
t.Skip("OPENSHELL_GATEWAY not set")
}
return sharedClient
}

// waitForPersistenceReady polls the gateway until its persistence layer is
// initialized, tolerating the transient errors observed right after the
// gateway process starts (transport not yet listening, sqlite migrations
// not yet applied).
func waitForPersistenceReady(client *v1.Client) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
defer cancel()

var lastErr error
for range 60 {
_, err := client.Sandboxes().ListAll(ctx, "default", v1.ListOptions{PageSize: 1})
if err == nil {
return nil
}
lastErr = err
if v1.IsUnavailable(err) {
time.Sleep(2 * time.Second)
continue
}
if strings.Contains(err.Error(), "no such table: objects") {
time.Sleep(1 * time.Second)
continue
}
return fmt.Errorf("unexpected error waiting for persistence: %w", err)
}
return fmt.Errorf("openshell-server persistence is not initialized after 60 attempts: %w", lastErr)
}
Comment on lines +107 to +129

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: waitForPersistenceReady uses context.Background() with no deadline, so a hung gRPC call blocks TestMain indefinitely.

Why this matters: Line 105 creates ctx := context.Background(). The 60-iteration attempt cap on line 107 only guards against repeated short failures. If a single ListAll call hangs (TCP socket open but gateway process stuck), the loop blocks forever because the context has no deadline. This runs in TestMain before any test executes, so it blocks the whole binary with no diagnostics.

Suggested fix: ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute); defer cancel()

Source: production agent

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a three-minute deadline to gateway persistence readiness checks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed. harness_test.go:108 now wraps the readiness loop in context.WithTimeout(context.Background(), 3*time.Minute), so a hung ListAll can no longer block TestMain.


// uniqueName returns a process-unique resource name so parallel tests never
// collide on the same sandbox/workspace/provider name.
func uniqueName(prefix string) string {
return fmt.Sprintf("%s-%09d", prefix, time.Now().UnixNano()%1_000_000_000)
}
Comment on lines +133 to +135

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notable: Draft RFC 0015 (PR #3238) proposes shared scenario identifiers across SDK E2E suites and a task name of the form e2e:sdk:go. The current task is e2e:go and the tests carry no scenario identifiers.

Why this matters: The RFC is not normative yet so no action is required. The file-per-domain structure (sandbox, workspace, providers) aligns with the RFC's proposed groupings, which would ease a later migration.

Suggested fix: No fix now. If RFC 0015 is adopted, rename the task and annotate each test with its scenario identifier.

Source: architecture agent

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. The current task split predates the RFC contract; it still needs e2e:sdk:go:adapter, Docker-backed e2e:sdk:go, and the e2e:go alias.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged, and your read matches mine. Nothing to do here until RFC 0015 is accepted; the task split and scenario identifiers belong in the same change that introduces the adapter contract.


func requireSandboxResponse(t *testing.T, sandbox *v1.Sandbox, workspace, name string) {
t.Helper()
require.NotNil(t, sandbox)
require.NotEmpty(t, sandbox.ID)
require.Equal(t, workspace, sandbox.Workspace)
require.Equal(t, name, sandbox.Name)
require.False(t, sandbox.CreatedAt.IsZero())
require.NotZero(t, sandbox.ResourceVersion)
}

func requireReadySandbox(t *testing.T, sandbox *v1.Sandbox, id string) {
t.Helper()
require.NotNil(t, sandbox)
require.Equal(t, id, sandbox.ID)
require.Equal(t, v1.SandboxReady, sandbox.Status.Phase)
}

func readEnvVar(ctx context.Context, client *v1.Client, workspace, sandboxName, key string) (string, error) {
command := []string{"sh", "-c", `if value=$(printenv "$1"); then printf "%s" "$value"; else printf NOT_SET; fi`, "--", key}
result, err := client.Exec().Run(ctx, workspace, sandboxName, command, v1.ExecOptions{})
// A supervisor reconnect can make one reverse relay miss its opening window.
// Retry only that transport timeout; repeated failures still fail the test.
if err != nil && ctx.Err() == nil && v1.IsDeadlineExceeded(err) && strings.Contains(err.Error(), "relay open timed out") {
select {
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(250 * time.Millisecond):
}
result, err = client.Exec().Run(ctx, workspace, sandboxName, command, v1.ExecOptions{})
}
if err != nil {
return "", err
}
if result.ExitCode != 0 {
return "", fmt.Errorf("exec exited %d: %s", result.ExitCode, string(result.Stderr))
}
if len(result.Stderr) != 0 {
return "", fmt.Errorf("exec wrote unexpected stderr: %s", string(result.Stderr))
}
return string(result.Stdout), nil
}

func isPlaceholderForEnvKey(value, key string) bool {
const prefix = "openshell:resolve:env:"
suffix, ok := strings.CutPrefix(value, prefix)
if !ok {
return false
}
if suffix == key {
return true
}

discriminator, placeholderKey, ok := strings.Cut(suffix, "_")
if !ok || placeholderKey != key || len(discriminator) < 2 {
return false
}

switch discriminator[0] {
case 'v':
for _, char := range discriminator[1:] {
if char < '0' || char > '9' {
return false
}
}
return true
case 's':
if len(discriminator) != 65 {
return false
}
for _, char := range discriminator[1:] {
if !((char >= '0' && char <= '9') || (char >= 'a' && char <= 'f')) {
return false
}
}
return true
default:
return false
}
}

func TestIsPlaceholderForEnvKey(t *testing.T) {
stableHandle := strings.Repeat("a1", 32)
tests := []struct {
name string
value string
key string
want bool
}{
{name: "plain", value: "openshell:resolve:env:API_KEY", key: "API_KEY", want: true},
{name: "revisioned", value: "openshell:resolve:env:v12_API_KEY", key: "API_KEY", want: true},
{name: "stable handle", value: "openshell:resolve:env:s" + stableHandle + "_API_KEY", key: "API_KEY", want: true},
{name: "overlapping key suffix", value: "openshell:resolve:env:v12_LONG_API_KEY", key: "API_KEY", want: false},
{name: "revision requires digits", value: "openshell:resolve:env:vnext_API_KEY", key: "API_KEY", want: false},
{name: "stable handle requires lowercase hex", value: "openshell:resolve:env:s" + strings.ToUpper(stableHandle) + "_API_KEY", key: "API_KEY", want: false},
{name: "stable handle requires 64 characters", value: "openshell:resolve:env:sabc_API_KEY", key: "API_KEY", want: false},
{name: "wrong key", value: "openshell:resolve:env:v12_OTHER_KEY", key: "API_KEY", want: false},
{name: "raw secret", value: "secret", key: "API_KEY", want: false},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.want, isPlaceholderForEnvKey(test.value, test.key))
})
}
}

// pollUntil calls fetch until the predicate is satisfied, the local polling
// timeout expires, or ctx is done, whichever happens first.
func pollUntil[T any](t *testing.T, ctx context.Context, fetch func() T, predicate func(T) bool) T {
t.Helper()
deadline := time.NewTimer(35 * time.Second)
defer deadline.Stop()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()

for {
value := fetch()
if predicate(value) {
return value
}
select {
case <-ctx.Done():
t.Fatalf("context done while polling; last observed %v: %v", value, ctx.Err())
case <-deadline.C:
t.Fatalf("timed out waiting for expected value, last observed %v", value)
case <-ticker.C:
}
}
}

// defaultPolicy returns a baseline sandbox policy sufficient for exec-based tests.
func defaultPolicy() *v1.SandboxPolicy {
return &v1.SandboxPolicy{
Version: 1,
Filesystem: &v1.FilesystemPolicy{
IncludeWorkdir: true,
ReadOnly: []string{"/usr", "/lib", "/lib64", "/proc", "/etc", "/app", "/dev/urandom"},
ReadWrite: []string{"/sandbox", "/tmp", "/dev/null"},
},
Landlock: &v1.LandlockPolicy{Compatibility: "best_effort"},
Process: &v1.ProcessPolicy{RunAsUser: "10001", RunAsGroup: "10001"},
}
}
Loading
Loading