diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-09-24 18:44:14 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-09-24 18:44:14 +0900 |
| commit | 1c4f593566943d8a0ce16eb3cae77f8c50238202 (patch) | |
| tree | 2ac5c41a84127ff4cd5a275c81e834c495a109c9 /internal/app | |
| parent | b99c63337df4068b36d7d8de2ae79fd274172977 (diff) | |
internal/app: unexport outcome, remove app struct
The App struct no longer does anything, and the outcome struct is entirely opaque.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app')
| -rw-r--r-- | internal/app/app.go | 81 | ||||
| -rw-r--r-- | internal/app/app_test.go | 53 | ||||
| -rw-r--r-- | internal/app/export_test.go | 13 | ||||
| -rw-r--r-- | internal/app/process.go | 9 | ||||
| -rw-r--r-- | internal/app/seal.go | 8 | ||||
| -rw-r--r-- | internal/app/strings.go | 5 |
6 files changed, 47 insertions, 122 deletions
diff --git a/internal/app/app.go b/internal/app/app.go index dd34cf62..4ecf1cc0 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -3,83 +3,28 @@ package app import ( "context" - "fmt" "log" - "sync" + "os" "hakurei.app/hst" "hakurei.app/internal/app/state" "hakurei.app/internal/sys" ) -// New returns the address of a newly initialised [App] struct. -func New(ctx context.Context, os sys.State) (*App, error) { - a := new(App) - a.sys = os - a.ctx = ctx - - id := new(state.ID) - err := state.NewAppID(id) - a.id = newID(id) - - return a, err -} - -// MustNew calls [New] and panics if an error is returned. -func MustNew(ctx context.Context, os sys.State) *App { - a, err := New(ctx, os) - if err != nil { - log.Fatalf("cannot create app: %v", err) - } - return a -} - -// An App keeps track of the hakurei container lifecycle. -type App struct { - outcome *Outcome - - id *stringPair[state.ID] - sys sys.State - ctx context.Context - mu sync.RWMutex -} - -// ID returns a copy of [state.ID] held by App. -func (a *App) ID() state.ID { a.mu.RLock(); defer a.mu.RUnlock(); return a.id.unwrap() } - -func (a *App) String() string { - if a == nil { - return "<nil>" +// Main runs an app according to [hst.Config] and terminates. Main does not return. +func Main(ctx context.Context, k sys.State, config *hst.Config) { + var id state.ID + if err := state.NewAppID(&id); err != nil { + log.Fatal(err) } - a.mu.RLock() - defer a.mu.RUnlock() - - if a.outcome != nil { - if a.outcome.user.uid == nil { - return "<invalid>" - } - return fmt.Sprintf("sealed app %s as uid %s", a.id, a.outcome.user.uid) + var seal outcome + seal.id = &stringPair[state.ID]{id, id.String()} + if err := seal.finalise(ctx, k, config); err != nil { + printMessageError("cannot seal app:", err) + os.Exit(1) } - return fmt.Sprintf("unsealed app %s", a.id) -} - -// Seal determines the [Outcome] of [hst.Config]. -// Values stored in and referred to by [hst.Config] might be overwritten and must not be used again. -func (a *App) Seal(config *hst.Config) (*Outcome, error) { - a.mu.Lock() - defer a.mu.Unlock() - - if a.outcome != nil { - panic("attempting to seal app twice") - } - - seal := new(Outcome) - seal.id = a.id - err := seal.finalise(a.ctx, a.sys, config) - if err == nil { - a.outcome = seal - } - return seal, err + seal.main() + panic("unreachable") } diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 8b54b4e2..edd8c9f0 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -16,12 +16,12 @@ import ( ) type sealTestCase struct { - name string - os sys.State - config *hst.Config - id state.ID - wantSys *system.I - wantContainer *container.Params + name string + os sys.State + config *hst.Config + id state.ID + wantSys *system.I + wantParams *container.Params } func TestApp(t *testing.T) { @@ -29,38 +29,27 @@ func TestApp(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - a := app.NewWithID(t.Context(), tc.id, tc.os) - var ( - gotSys *system.I - gotContainer *container.Params - ) - if !t.Run("seal", func(t *testing.T) { - if sa, err := a.Seal(tc.config); err != nil { + t.Run("finalise", func(t *testing.T) { + sys, params, err := app.FinaliseIParams(t.Context(), tc.os, tc.config, &tc.id) + if err != nil { if s, ok := container.GetErrorMessage(err); !ok { - t.Errorf("Seal: error = %v", err) + t.Fatalf("Seal: error = %v", err) } else { - t.Errorf("Seal: %s", s) + t.Fatalf("Seal: %s", s) } - return - } else { - gotSys, gotContainer = app.AppIParams(a, sa) } - }) { - return - } - t.Run("compare sys", func(t *testing.T) { - if !gotSys.Equal(tc.wantSys) { - t.Errorf("Seal: sys = %#v, want %#v", - gotSys, tc.wantSys) - } - }) + t.Run("sys", func(t *testing.T) { + if !sys.Equal(tc.wantSys) { + t.Errorf("Seal: sys = %#v, want %#v", sys, tc.wantSys) + } + }) - t.Run("compare params", func(t *testing.T) { - if !reflect.DeepEqual(gotContainer, tc.wantContainer) { - t.Errorf("seal: params =\n%s\n, want\n%s", - mustMarshal(gotContainer), mustMarshal(tc.wantContainer)) - } + t.Run("params", func(t *testing.T) { + if !reflect.DeepEqual(params, tc.wantParams) { + t.Errorf("seal: params =\n%s\n, want\n%s", mustMarshal(params), mustMarshal(tc.wantParams)) + } + }) }) }) } diff --git a/internal/app/export_test.go b/internal/app/export_test.go index 3e5db9fb..1bfba48a 100644 --- a/internal/app/export_test.go +++ b/internal/app/export_test.go @@ -4,18 +4,13 @@ import ( "context" "hakurei.app/container" + "hakurei.app/hst" "hakurei.app/internal/app/state" "hakurei.app/internal/sys" "hakurei.app/system" ) -func NewWithID(ctx context.Context, id state.ID, os sys.State) *App { - return &App{id: newID(&id), sys: os, ctx: ctx} -} - -func AppIParams(a *App, seal *Outcome) (*system.I, *container.Params) { - if a.outcome != seal || a.id != seal.id { - panic("broken app/outcome link") - } - return seal.sys, seal.container +func FinaliseIParams(ctx context.Context, k sys.State, config *hst.Config, id *state.ID) (*system.I, *container.Params, error) { + seal := outcome{id: &stringPair[state.ID]{*id, id.String()}} + return seal.sys, seal.container, seal.finalise(ctx, k, config) } diff --git a/internal/app/process.go b/internal/app/process.go index 0ee123a1..a182fa6c 100644 --- a/internal/app/process.go +++ b/internal/app/process.go @@ -22,7 +22,7 @@ import ( // duration to wait for shim to exit, after container WaitDelay has elapsed. const shimWaitTimeout = 5 * time.Second -// mainState holds persistent state bound to [Outcome.Main]. +// mainState holds persistent state bound to outcome.main. type mainState struct { // done is whether beforeExit has been called already. done bool @@ -33,7 +33,7 @@ type mainState struct { // Time is nil if no process was ever created. Time *time.Time - seal *Outcome + seal *outcome store state.Store cancel context.CancelFunc cmd *exec.Cmd @@ -218,9 +218,8 @@ func (ms mainState) fatal(fallback string, ferr error) { os.Exit(1) } -// Main commits deferred system setup, runs the container, reverts changes to the system, and terminates the program. -// Main does not return. -func (seal *Outcome) Main() { +// main carries out outcome and terminates. main does not return. +func (seal *outcome) main() { if !seal.f.CompareAndSwap(false, true) { panic("outcome: attempted to run twice") } diff --git a/internal/app/seal.go b/internal/app/seal.go index cafd6915..2d7fea39 100644 --- a/internal/app/seal.go +++ b/internal/app/seal.go @@ -32,8 +32,8 @@ func newWithMessageError(msg string, err error) error { return &hst.AppError{Step: "finalise", Err: err, Msg: msg} } -// An Outcome is the runnable state of a hakurei container via [hst.Config]. -type Outcome struct { +// An outcome is the runnable state of a hakurei container via [hst.Config]. +type outcome struct { // copied from initialising [app] id *stringPair[state.ID] // copied from [sys.State] @@ -66,7 +66,7 @@ type shareHost struct { // process-specific directory in XDG_RUNTIME_DIR, empty if unused runtimeSharePath *container.Absolute - seal *Outcome + seal *outcome sc hst.Paths } @@ -119,7 +119,7 @@ type hsuUser struct { username string } -func (seal *Outcome) finalise(ctx context.Context, k sys.State, config *hst.Config) error { +func (seal *outcome) finalise(ctx context.Context, k sys.State, config *hst.Config) error { const ( home = "HOME" shell = "SHELL" diff --git a/internal/app/strings.go b/internal/app/strings.go index e4465cee..a7fdcd46 100644 --- a/internal/app/strings.go +++ b/internal/app/strings.go @@ -2,12 +2,9 @@ package app import ( "strconv" - - "hakurei.app/internal/app/state" ) -func newInt(v int) *stringPair[int] { return &stringPair[int]{v, strconv.Itoa(v)} } -func newID(id *state.ID) *stringPair[state.ID] { return &stringPair[state.ID]{*id, id.String()} } +func newInt(v int) *stringPair[int] { return &stringPair[int]{v, strconv.Itoa(v)} } // stringPair stores a value and its string representation. type stringPair[T comparable] struct { |
