Skip to content
Merged
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
257 changes: 257 additions & 0 deletions internal/dockerdeploy/mapped_conformance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package dockerdeploy

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"reflect"
"runtime"
"testing"

"github.com/omry/reploy/internal/deploy"
)

// These journeys use production planners, locks, leases, stream framing and
// lifetime code. Engine, process pinning, native admission and F observations
// remain injected. The separate probe suite exercises the real exec mechanism.
func TestMappedConformanceFreshJourneyV1(t *testing.T) {
for _, profile := range []string{"exact", "bounded"} {
for _, entry := range []struct {
name string
kind mappedEntryKindV1
}{
{"workload", mappedWorkloadStartV1},
{"application", mappedApplicationCommandV1},
{"runtime-test", mappedRuntimeTestV1},
{"hook", mappedLifecycleHookV1},
} {
for repetition := range 3 {
t.Run(fmt.Sprintf("%s/%s/%d", profile, entry.name, repetition), func(t *testing.T) {
f := newMappedLiveFixtureV1(t, profile, entry.kind)
var stdout, stderr bytes.Buffer
f.waiter.options.Stdout, f.waiter.options.Stderr = &stdout, &stderr
original := f.waiter.backend.run
f.waiter.backend.run = func(s CommandSpec, o RunOptions) error {
err := original(s, o)
if err == nil && s.Args[0] == "start" {
if _, err = io.WriteString(o.Stdout, "application output\n"); err == nil {
_, err = io.WriteString(o.Stderr, "application diagnostic\n")
}
}
return err
}
f.beforeRecovery = func(op *deploy.OperationLock) {
if err := op.RequireQueueEntryLeaseHeldV1(f.run.ID); err != nil {
t.Error("owned lease lost before recovery", err)
}
}
err := runMappedControlledEntryWithBackendV1(f.waiter.ctx, f.directory, f.waiter.prepared.ops, f.run.ID, f.input, f.waiter.options, f.backend)
if entry.kind == mappedWorkloadStartV1 {
if !errors.Is(err, errMappedMasterExitedV1) {
t.Fatalf("required master exit lost: %v", err)
}
} else if err != nil {
t.Fatal(err)
}
_, queue, state := f.inspect(t)
if len(queue.Runs) != 0 || state.IdentityMapping.PendingOperation != nil || f.starts != 1 || f.recoveries != 1 || f.waiter.created != 1 || f.waiter.admissions != 1 || f.waiter.removed != 1 || f.waiter.process.closed != 1 {
t.Fatal("journey did not release and retire exactly one owned instance")
}
if f.waiter.application.Load() == 0 || stdout.String() != "application output\n" || stderr.String() != "application diagnostic\n" {
t.Fatal("application streams lost, mixed, or contain release metadata")
}
if state.IdentityMapping.AcceptedUIDs == nil || len(*state.IdentityMapping.AcceptedUIDs) == 0 || !reflect.DeepEqual(state.IdentityMapping.UIDAssignments, f.input.Current.State.IdentityMapping.PendingOperation.Native.Active.UIDs) {
t.Fatal("retirement discarded admitted identity history")
}
select {
case frame := <-f.waiter.frame:
if frame.Binding != f.plan.binding || frame.Container != f.waiter.id {
t.Fatal("release substituted generation, role, image or plan")
}
default:
t.Fatal("missing release frame")
}
select {
case <-f.waiter.frame:
t.Fatal("duplicate release frame")
default:
}
facts, err := f.plan.facts()
if err != nil {
t.Fatal(err)
}
evidence, err := json.Marshal(struct {
Profile, Entry, Compiler, OS, Architecture string
Facts mappedEntryPayloadV1
Qualification string
}{profile, entry.name, runtime.Version(), runtime.GOOS, runtime.GOARCH, facts, "private-orchestration; injected engine/native/F"})
if err != nil {
t.Fatal(err)
}
t.Logf("E conformance receipt: %s", evidence)
})
}
}
}
}

func TestMappedConformanceRefusalJourneyV1(t *testing.T) {
for _, profile := range []string{"exact", "bounded"} {
for _, mode := range []string{"role", "image", "revision", "runtime-account", "maps", "owner", "occupied", "host-native"} {
t.Run(profile+"/"+mode, func(t *testing.T) {
f := newMappedLiveFixtureV1(t, profile, mappedApplicationCommandV1)
op := f.waiter.prepared.ops.Operation
before, _, err := op.ReadStateV1()
if err != nil {
t.Fatal(err)
}
switch mode {
case "role":
f.input.Command = "undeclared-role"
case "image":
f.input.Runtime.Image += "-stale"
case "revision":
f.input.Current.State.IdentityMapping.Revision = "99"
case "runtime-account":
f.input.Current.State.IdentityMapping.PendingOperation.Native.RuntimeUsers = []string{"999"}
case "maps":
f.input.Current.State.IdentityMapping.PendingOperation.Native.Active.UIDs[0].HostStart = "999999"
case "owner":
f.input.Current.State.IdentityMapping.PendingOperation.Native.Engine = "foreign"
case "occupied":
f.input.Kind = mappedControlledSessionV1
case "host-native":
f.input.Kind = mappedHostNativeCommandV1
}
if err := runMappedControlledEntryWithBackendV1(f.waiter.ctx, f.directory, f.waiter.prepared.ops, f.run.ID, f.input, f.waiter.options, f.backend); err == nil {
t.Fatal("invalid authority accepted")
}
if f.starts != 0 || f.waiter.created != 0 || f.waiter.admissions != 0 || f.waiter.application.Load() != 0 || f.recoveries != 0 {
t.Fatal("refusal reached an engine or application effect")
}
// Pure construction keeps the caller lock. Once construction
// succeeds, the lifetime runner owns and releases that lock even
// when its durable owner check refuses before create.
if op.RequireHeld() != nil {
op, err = deploy.AcquireOperationLock(t.Context(), f.directory)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = op.Unlock() })
}
after, _, err := op.ReadStateV1()
if err != nil || !reflect.DeepEqual(before, after) {
t.Fatal("refusal changed durable state", err)
}
})
}
}
}

func TestMappedConformanceInterruptedJourneyV1(t *testing.T) {
for _, profile := range []string{"exact", "bounded"} {
for _, mode := range []string{"engine", "create", "pin", "pid", "F", "sandbox", "admission", "partial-frame", "cleanup", "retirement"} {
t.Run(profile+"/"+mode, func(t *testing.T) {
f := newMappedLiveFixtureV1(t, profile, mappedApplicationCommandV1)
w := f.waiter
switch mode {
case "engine":
w.observeFailure = true
case "create":
w.createFailure = true
case "pin":
w.pinFailure = true
case "pid":
w.process.fail = true
case "F":
w.effect.fail = true
case "sandbox":
w.effect.sandboxFailure = errors.New("missing trusted kernel/code observation")
case "admission":
w.backend.admit = func(deploy.IdentityMappingOperationsV1, context.Context, deploy.StateV1, string) (deploy.StateV1, error) {
return deploy.StateV1{}, errors.New("uncertain native admission")
}
case "partial-frame":
original := w.backend.run
w.backend.run = func(s CommandSpec, o RunOptions) error {
if s.Args[0] != "start" {
return original(s, o)
}
_, err := io.ReadFull(o.Stdin, make([]byte, 7))
return errors.Join(err, errors.New("release interrupted"))
}
case "cleanup":
w.cleanupFailure = true
case "retirement":
f.retirementError = errors.New("retirement proof unavailable")
}
if err := runMappedControlledEntryWithBackendV1(w.ctx, f.directory, w.prepared.ops, f.run.ID, f.input, w.options, f.backend); err == nil {
t.Fatal("interrupted journey claimed success")
}
_, queue, state := f.inspect(t)
if state.IdentityMapping.PendingOperation == nil || len(queue.Runs) != 1 || w.created > 1 || w.admissions > 1 || f.recoveries > 1 {
t.Fatal("uncertainty discarded ownership or retried effects")
}
if w.created == 1 && mode != "create" && queue.Runs[0].Container != w.id && w.removed != 1 {
t.Fatal("uncertainty neither retained nor cleaned the exact instance")
}
if mode != "cleanup" && mode != "retirement" {
if w.application.Load() != 0 {
t.Fatal("refused or incomplete release reached application input")
}
select {
case <-w.frame:
t.Fatal("refused journey delivered a complete release frame")
default:
}
}
})
}
}
}

func TestMappedConformanceCancellationJourneyV1(t *testing.T) {
for _, profile := range []string{"exact", "bounded"} {
t.Run(profile, func(t *testing.T) {
f := newMappedLiveFixtureV1(t, profile, mappedApplicationCommandV1)
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
f.waiter.options.Context, f.untilCancel = ctx, true
result := make(chan error, 1)
go func() {
result <- runMappedControlledEntryWithBackendV1(f.waiter.ctx, f.directory, f.waiter.prepared.ops, f.run.ID, f.input, f.waiter.options, f.backend)
}()
select {
case <-f.waiter.frame:
case <-f.waiter.ctx.Done():
t.Fatal("release did not complete within admission deadline")
}
op, err := deploy.AcquireOperationLock(f.waiter.ctx, f.directory)
if err != nil {
t.Fatal(err)
}
queue, _, err := op.ReadLiveRunQueueV1()
owned := err == nil && len(queue.Runs) == 1 && queue.Runs[0].Container == f.waiter.id && op.RequireQueueEntryLeaseHeldV1(f.run.ID) == nil
_ = op.Unlock()
if !owned {
t.Fatal("attached run did not retain exact ownership and lease", err)
}
cancel()
select {
case err := <-result:
if !errors.Is(err, context.Canceled) {
t.Fatalf("cancellation lost: %v", err)
}
case <-f.waiter.ctx.Done():
t.Fatal("cancellation did not finish within fixture deadline")
}
_, queue, state := f.inspect(t)
if len(queue.Runs) != 0 || state.IdentityMapping.PendingOperation != nil || f.waiter.removed != 1 || f.recoveries != 1 || state.IdentityMapping.AcceptedUIDs == nil || len(*state.IdentityMapping.AcceptedUIDs) == 0 {
t.Fatal("cancelled journey lost cleanup or admitted mapping history")
}
})
}
}
19 changes: 19 additions & 0 deletions internal/probe/mapped_exec_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,9 @@ func TestMappedDescriptorRaceHelperV1(t *testing.T) {
panic(err)
}
close(ready)
if args[0] == "descriptor-shared-close" {
mappedSharedCloseObserverV1(&damaged)
}
for {
if _, _, errno := syscall.RawSyscall(unix.SYS_FCNTL, 4096, unix.F_GETFD, 0); errno == unix.EBADF {
atomic.StoreUint32(&damaged, 1)
Expand Down Expand Up @@ -645,6 +648,22 @@ func TestMappedDescriptorRaceHelperV1(t *testing.T) {
os.Exit(Main(args[2:], os.Stdin, os.Stdout, os.Stderr))
}

// The deliberately wrong shared closer damages runtime descriptors too. Its
// observer must not depend on Go scheduling or descriptor operations afterward.
// Other modes retain the real concurrent opener above.
//
//go:nosplit
//go:norace
func mappedSharedCloseObserverV1(damaged *uint32) {
for {
if _, _, errno := syscall.RawSyscall(unix.SYS_FCNTL, 4096, unix.F_GETFD, 0); errno == unix.EBADF {
atomic.StoreUint32(damaged, 1)
for {
}
}
}
}

// Deliberately wrong test-only closer. A peer detects the shared table's lost
// high fd. Stay in a raw tail so a runtime poller failure cannot masquerade as
// that specific independent observation. The parent bounds/reaps the process.
Expand Down
56 changes: 56 additions & 0 deletions tools/conformance/multiidentity/E_CONFORMANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Private milestone E conformance

Run from the Reploy checkout with a native Go toolchain. Use a new output
directory under an existing private evidence parent:

```sh
python3 tools/conformance/multiidentity/e_conformance.py --output-dir /absolute/new/evidence
python3 -m unittest discover -s tools/conformance/multiidentity -p test_e_conformance.py
```

The runner creates no engine resources. Its existing production-startup tests
use disposable Linux user/network namespaces and bounded child processes.
They need `cc`, `unshare`, `newuidmap`, `newgidmap` and delegated subordinate
IDs. Missing prerequisites remain visible test skips, not qualification.
Mapped runtime support remains limited by the approved Linux/Podman cohort;
running the portable fixture suites elsewhere does not expand that cohort.

`report.json` binds source bytes and file modes, Go configuration and compiler
and linker hashes, host kernel/architecture, fixture helpers and the pinned
OCI seccomp seed. It checks source/toolchain stability after collection.
Each suite has its exact command, elapsed time, JSON event log digest and
complete terminal test statuses. Only stdout is parsed as JSON. Each suite's
stderr is retained separately in `<suite>.stderr` with its own digest, including
partial timeout diagnostics. The two metadata commands retain `go-env-before.stderr`
and `go-env-after.stderr`; their digests are recorded separately from toolchain facts.
Private fresh journeys also emit their exact
closed plan, including profile, maps, immutable image, role, generation,
runtime/kernel/network policies and stream mode. These are fixture facts.
The actual production exec tests identify their compiled source/toolchain
and verify the production exec tail; this is mechanism evidence, not a
retained launched-image binary or live-engine receipt.

The integrated private journey repeats workload, application, runtime-test and
hook cases three times for each of `exact` and `bounded`. It checks one release,
separate application streams, owned completion/cleanup and retained mapping
history. Invalid authority and interrupted proof/release paths check absence of
application effects. Existing suites cover native atomic admission, canonical
policy, capabilities, private/high/concurrent descriptors, occupied refusal,
replacement refusal, cancellation and cleanup ownership.

Collection exit zero means the required private suites passed, their fresh
matrix is complete, no selected test failed and the source/toolchain stayed
stable. A partially skipped production-exec suite remains `partial`, with
each gap listed; it cannot become a full kernel qualification. A missing
required test, failed test, timeout, malformed log or changed source fails
collection. Existing evidence directories are never overwritten.

No result from this runner qualifies a live engine or public mapped execution.
`mapped_runtime_qualified` is always false. Production F code/mount/network
observations, an exact engine/OCI-policy receipt, unresolved kernel tuples and
G activation remain separate gates. Working replacement and occupied exec
remain refused. The older `conformance.py` Podman probe is a distinct lower
level experiment and cannot stand in for this integrated E evidence.
The opt-in `TestMappedPodmanCreateLiveV1` is explicitly excluded from this
private runner, even when its enabling environment is set. The exact exclusion
is retained in the report and command; live-engine qualification stays open.
Loading
Loading