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
6 changes: 5 additions & 1 deletion internal/dockerdeploy/current_app_command_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ func runCurrentAppCommandV1(ctx context.Context, input CurrentAppCommandRunInput
if input.DeployedOnly && state.Deployment == nil {
return fmt.Errorf("deployed-only app commands require an installed deployment")
}
document, err := unmappedRuntimeDocumentV1(state)
kind := mappedApplicationCommandV1
if input.DeployedOnly {
kind = mappedDeployedCommandV1
}
document, err := unmappedRuntimeDocumentForEntryV1(state, kind)
if err != nil {
return fmt.Errorf("runtime blueprint: %w", err)
}
Expand Down
5 changes: 4 additions & 1 deletion internal/dockerdeploy/current_controlled_session_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ func runCurrentControlledSessionV1(
if err != nil {
return result, fmt.Errorf("controlled-session workload runtime: %w", err)
}
if err := requireUnmappedSessionOwnersV1(controller.current.State, workload.current.State); err != nil {
return result, err
}
for _, runtime := range []struct {
role string
dir string
Expand Down Expand Up @@ -426,7 +429,7 @@ func loadCurrentControlledSessionRuntimeV1(
if !found {
return currentControlledSessionRuntimeV1{}, fmt.Errorf("runtime state is missing; run `reploy stage` or `reploy install`")
}
document, err := unmappedRuntimeDocumentV1(state)
document, err := unmappedRuntimeDocumentForEntryV1(state, mappedControlledSessionV1)
if err != nil {
return currentControlledSessionRuntimeV1{}, fmt.Errorf("runtime blueprint: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/dockerdeploy/current_controlled_session_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,14 @@ func TestRunCurrentControlledSessionV1RejectsConfiguredPrivateEnvironmentBeforeP
t.Fatalf("create private environment = %t, %v", created, err)
}
planned := false
current, _ := runtimeCurrentBuildFixture(t)
_, err := runCurrentControlledSessionV1(t.Context(), CurrentControlledSessionRunInputV1{
ControllerDeploymentDir: controllerDir,
WorkloadDeploymentDir: workloadDir,
}, currentControlledSessionRunBackendV1{
acquire: deploy.AcquireOperationLock,
loadRuntime: func(context.Context, *deploy.OperationLock, string, StagedProviderBuildRuntimeV1) (currentControlledSessionRuntimeV1, error) {
return currentControlledSessionRuntimeV1{}, nil
return currentControlledSessionRuntimeV1{current: current}, nil
},
privateEnv: preparePrivateWorkloadEnvironmentV1,
prepareController: preparedControlledSessionControllerForTestV1,
Expand Down
2 changes: 1 addition & 1 deletion internal/dockerdeploy/current_runtime_test_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func runCurrentRuntimeTestV1(ctx context.Context, input CurrentRuntimeTestInputV
if !found {
return fmt.Errorf("runtime state is missing; run `reploy stage` or `reploy install`")
}
document, err := unmappedRuntimeDocumentV1(state)
document, err := unmappedRuntimeDocumentForEntryV1(state, mappedRuntimeTestV1)
if err != nil {
return fmt.Errorf("runtime blueprint: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/dockerdeploy/current_shell_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func runCurrentShellV1(ctx context.Context, input CurrentShellRunInputV1, backen
if !found {
return fmt.Errorf("runtime state is missing; run `reploy stage` or `reploy install`")
}
document, err := unmappedRuntimeDocumentV1(state)
document, err := unmappedRuntimeDocumentForEntryV1(state, mappedShellV1)
if err != nil {
return fmt.Errorf("runtime blueprint: %w", err)
}
Expand Down
3 changes: 3 additions & 0 deletions internal/dockerdeploy/current_workload_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ type currentWorkloadLifecycleBackendV1 struct {
}

func RunCurrentWorkloadLifecycleV1(ctx context.Context, input CurrentWorkloadLifecycleInputV1) error {
if _, err := unmappedRuntimeDocumentForEntryV1(input.Current.State, mappedLifecycleHookV1); err != nil {
return err
}
return runCurrentWorkloadLifecycleV1(ctx, input, currentWorkloadLifecycleBackendV1{
acquire: deploy.AcquireOperationLock,
planStart: func(plan CurrentRuntimePlanV1, current CurrentBuild) (LifecyclePlan, error) {
Expand Down
4 changes: 2 additions & 2 deletions internal/dockerdeploy/current_workload_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func runCurrentWorkloadV1(ctx context.Context, input CurrentWorkloadRunInputV1,
if !found {
return fmt.Errorf("runtime state is missing; run `reploy stage` or `reploy install`")
}
if _, err := unmappedRuntimeDocumentV1(state); err != nil {
if _, err := unmappedRuntimeDocumentForEntryV1(state, mappedWorkloadEntryKindV1(input.Action)); err != nil {
return err
}
recoverStop := func(cause error) error {
Expand Down Expand Up @@ -182,7 +182,7 @@ func runCurrentWorkloadV1(ctx context.Context, input CurrentWorkloadRunInputV1,
if state.Current == nil {
return recoverStop(fmt.Errorf("%s", currentBuildRecoveryMessageV1(state, "runtime build is missing")))
}
document, err := unmappedRuntimeDocumentV1(state)
document, err := unmappedRuntimeDocumentForEntryV1(state, mappedWorkloadEntryKindV1(input.Action))
if err != nil {
return recoverStop(fmt.Errorf("runtime blueprint: %w", err))
}
Expand Down
8 changes: 6 additions & 2 deletions internal/dockerdeploy/live_run_mapped.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ type mappedLiveRunBackendV1 struct {
// Close must interrupt Read. Borrowed readers are refused before engine effects.
// It is private plumbing: no public mapped guard or F qualification is supplied.
func runMappedLiveInstanceV1(admission context.Context, directory string, ops deploy.IdentityMappingOperationsV1, runID string, plan mappedEntryPlanV1, options RunOptions) error {
return runMappedLiveInstanceWithBackendV1(admission, directory, ops, runID, plan, options, mappedLiveRunBackendV1{
return runMappedLiveInstanceWithBackendV1(admission, directory, ops, runID, plan, options, mappedLiveHostBackendV1())
}

func mappedLiveHostBackendV1() mappedLiveRunBackendV1 {
return mappedLiveRunBackendV1{
acquire: deploy.AcquireOperationLock,
start: func(ctx context.Context, ops deploy.IdentityMappingOperationsV1, state deploy.StateV1, active identitymapping.Geometry, operation string, spec CommandSpec, policy mappedSeccompPolicyV1, binding mappedlaunch.Binding, options RunOptions) (*ownedMappedWaiterV1, error) {
p, err := prepareMappedPodmanCreateV1(ctx, ops, state, active, operation, spec, policy)
Expand All @@ -63,7 +67,7 @@ func runMappedLiveInstanceV1(admission context.Context, directory string, ops de
recover: func(ops deploy.IdentityMappingOperationsV1, ctx context.Context, state deploy.StateV1, operation string) (deploy.StateV1, error) {
return ops.Recover(ctx, state, operation, false)
},
})
}
}

func runMappedLiveInstanceWithBackendV1(admission context.Context, directory string, ops deploy.IdentityMappingOperationsV1, runID string, plan mappedEntryPlanV1, options RunOptions, backend mappedLiveRunBackendV1) (failure error) {
Expand Down
97 changes: 97 additions & 0 deletions internal/dockerdeploy/mapped_entry_adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package dockerdeploy

import (
"context"
"errors"
"fmt"
"path/filepath"

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

// Public adapters name their actual operation before any runtime effect. Keep
// the public inhibition as well as the narrower occupied/host-native refusal.
// This is not an admission flag, and ordinary entries retain their old path.
func unmappedRuntimeDocumentForEntryV1(state deploy.StateV1, kind mappedEntryKindV1) (blueprint.Document, error) {
mode, err := mappedEntryModeForKindV1(kind)
if err != nil {
return blueprint.Document{}, err
}
document, err := unmappedRuntimeDocumentV1(state)
if err != nil {
_, modeErr := runtimeDocumentForEntryModeV1(state, mode)
return blueprint.Document{}, errors.Join(err, modeErr)
}
return document, nil
}

func requireUnmappedSessionOwnersV1(controller, workload deploy.StateV1) error {
var failures []error
for _, owner := range []struct {
name string
state deploy.StateV1
}{{"controller", controller}, {"workload", workload}} {
if _, err := unmappedRuntimeDocumentForEntryV1(owner.state, mappedControlledSessionV1); err != nil {
failures = append(failures, fmt.Errorf("controlled-session %s owner: %w", owner.name, err))
}
}
return errors.Join(failures...)
}

func mappedWorkloadEntryKindV1(action string) mappedEntryKindV1 {
if action == "restart" {
return mappedHostReplacementV1
}
return mappedWorkloadStartV1
}

// The existing trusted caller supplies held-lock current snapshots and owns the
// queue lease. Construction grants no authority; the shared runner revalidates
// native admission and owns release, completion and cleanup. No public caller
// reaches this private composition while the public guards remain inhibited.
func runMappedControlledEntryV1(admission context.Context, directory string, ops deploy.IdentityMappingOperationsV1, runID string, input mappedEntryInputV1, options RunOptions) error {
return runMappedControlledEntryWithBackendV1(admission, directory, ops, runID, input, options, mappedLiveHostBackendV1())
}

func runMappedControlledEntryWithBackendV1(admission context.Context, directory string, ops deploy.IdentityMappingOperationsV1, runID string, input mappedEntryInputV1, options RunOptions, backend mappedLiveRunBackendV1) error {
if input.Kind == mappedHostReplacementV1 {
return fmt.Errorf("mapped replacement requires exact retirement and fresh admission")
}
plan, err := constructMappedEntryPlanV1(input)
if err != nil {
return err
}
return runMappedLiveInstanceWithBackendV1(admission, directory, ops, runID, plan, options, backend)
}

// Replacement requires complete startup/retirement lifecycle composition. Until
// that integration is qualified, refuse before retiring the old instance. The
// caller retains its lock and all native, lifecycle and queue ownership.
func runMappedReplacementEntryV1(admission context.Context, directory string, ops deploy.IdentityMappingOperationsV1, runID string, reload func(context.Context, deploy.IdentityMappingOperationsV1) (mappedEntryInputV1, error), options RunOptions) error {
if ops.Operation == nil {
return fmt.Errorf("mapped replacement requires its operation lock")
}
if admission == nil || options.Context == nil || admission.Err() != nil || options.Context.Err() != nil {
return fmt.Errorf("mapped replacement requires live admission and lifetime contexts")
}
absolute, err := filepath.Abs(directory)
if err != nil || directory == "" || filepath.Dir(filepath.Dir(ops.Operation.Path())) != absolute {
return fmt.Errorf("mapped replacement lock belongs to a different deployment")
}
if err := ops.Operation.RequireQueueEntryLeaseHeldV1(runID); err != nil {
return err
}
queue, _, err := ops.Operation.ReadLiveRunQueueV1()
if err != nil {
return err
}
run, found := findLiveRunV1(queue, runID)
if !found || run.Status != deploy.LiveRunStatusActiveV1 || run.Container != "" {
return fmt.Errorf("mapped replacement requires an unstarted active live entry")
}
if _, err := requireMappedWorkloadOwnerV1(admission, ops); err != nil {
return err
}
return fmt.Errorf("mapped replacement lifecycle composition is not yet available")
}
Loading
Loading