aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-09-24 18:44:14 +0900
committerOphestra <cat@gensokyo.uk>2025-09-24 18:44:14 +0900
commit1c4f593566943d8a0ce16eb3cae77f8c50238202 (patch)
tree2ac5c41a84127ff4cd5a275c81e834c495a109c9 /internal
parentb99c63337df4068b36d7d8de2ae79fd274172977 (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')
-rw-r--r--internal/app/app.go81
-rw-r--r--internal/app/app_test.go53
-rw-r--r--internal/app/export_test.go13
-rw-r--r--internal/app/process.go9
-rw-r--r--internal/app/seal.go8
-rw-r--r--internal/app/strings.go5
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 {