Compare commits

..

2 Commits

Author SHA1 Message Date
b25ade5f3d
internal/store: rename compat interface
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m17s
Test / Hakurei (push) Successful in 3m9s
Test / Sandbox (race detector) (push) Successful in 4m3s
Test / Hpkg (push) Successful in 4m4s
Test / Flake checks (push) Successful in 1m25s
Test / Hakurei (race detector) (push) Successful in 4m54s
The new store implementation will be exported as Store.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-30 18:53:59 +09:00
ebdcff1049
internal/store: rename from state
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m9s
Test / Hakurei (push) Successful in 3m8s
Test / Hpkg (push) Successful in 4m2s
Test / Sandbox (race detector) (push) Successful in 4m7s
Test / Hakurei (race detector) (push) Successful in 4m55s
Test / Flake checks (push) Successful in 1m25s
This reduces collision with local variable names, and generally makes sense for the new store package, since it no longer specifies the state struct.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-30 18:43:55 +09:00
16 changed files with 91 additions and 100 deletions

View File

@ -20,7 +20,7 @@ import (
"hakurei.app/internal"
"hakurei.app/internal/env"
"hakurei.app/internal/outcome"
"hakurei.app/internal/state"
"hakurei.app/internal/store"
"hakurei.app/message"
"hakurei.app/system/dbus"
)
@ -322,7 +322,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
c.NewCommand("ps", "List active instances", func(args []string) error {
var sc hst.Paths
env.CopyPaths().Copy(&sc, new(outcome.Hsu).MustID(nil))
printPs(os.Stdout, time.Now().UTC(), state.NewMulti(msg, sc.RunDirPath), flagShort, flagJSON)
printPs(os.Stdout, time.Now().UTC(), store.NewMulti(msg, sc.RunDirPath), flagShort, flagJSON)
return errSuccess
}).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id")
}

View File

@ -13,7 +13,7 @@ import (
"hakurei.app/hst"
"hakurei.app/internal/env"
"hakurei.app/internal/outcome"
"hakurei.app/internal/state"
"hakurei.app/internal/store"
"hakurei.app/message"
)
@ -85,8 +85,8 @@ func tryIdentifier(msg message.Msg, name string) (config *hst.Config, entry *hst
return tryIdentifierEntries(msg, name, func() map[hst.ID]*hst.State {
var sc hst.Paths
env.CopyPaths().Copy(&sc, new(outcome.Hsu).MustID(nil))
s := state.NewMulti(msg, sc.RunDirPath)
if entries, err := state.Join(s); err != nil {
s := store.NewMulti(msg, sc.RunDirPath)
if entries, err := store.Join(s); err != nil {
msg.GetLogger().Printf("cannot join store: %v", err) // not fatal
return nil
} else {

View File

@ -14,7 +14,7 @@ import (
"hakurei.app/internal"
"hakurei.app/internal/env"
"hakurei.app/internal/outcome"
"hakurei.app/internal/state"
"hakurei.app/internal/store"
"hakurei.app/message"
)
@ -168,9 +168,9 @@ func printShowInstance(
}
// printPs writes a representation of active instances to output.
func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON bool) {
func printPs(output io.Writer, now time.Time, s store.Compat, short, flagJSON bool) {
var entries map[hst.ID]*hst.State
if e, err := state.Join(s); err != nil {
if e, err := store.Join(s); err != nil {
log.Fatalf("cannot join store: %v", err)
} else {
entries = e

View File

@ -6,7 +6,7 @@ import (
"time"
"hakurei.app/hst"
"hakurei.app/internal/state"
"hakurei.app/internal/store"
)
var (
@ -709,6 +709,6 @@ func TestPrintPs(t *testing.T) {
type stubStore map[hst.ID]*hst.State
func (s stubStore) Join() (map[hst.ID]*hst.State, error) { return s, nil }
func (s stubStore) Do(int, func(c state.Cursor)) (bool, error) { panic("unreachable") }
func (s stubStore) Do(int, func(c store.Cursor)) (bool, error) { panic("unreachable") }
func (s stubStore) List() ([]int, error) { panic("unreachable") }
func (s stubStore) Close() error { return nil }

View File

@ -15,7 +15,7 @@ import (
"hakurei.app/container/fhs"
"hakurei.app/hst"
"hakurei.app/internal"
"hakurei.app/internal/state"
"hakurei.app/internal/store"
"hakurei.app/message"
"hakurei.app/system"
)
@ -34,7 +34,7 @@ type mainState struct {
// Time is nil if no process was ever created.
Time *time.Time
store state.Store
store store.Compat
cancel context.CancelFunc
cmd *exec.Cmd
cmdWait chan error
@ -127,7 +127,7 @@ func (ms mainState) beforeExit(isFault bool) {
}
if ms.uintptr&mainNeedsRevert != 0 {
if ok, err := ms.store.Do(ms.k.state.identity.unwrap(), func(c state.Cursor) {
if ok, err := ms.store.Do(ms.k.state.identity.unwrap(), func(c store.Cursor) {
if ms.uintptr&mainNeedsDestroy != 0 {
if err := c.Destroy(ms.k.state.id.unwrap()); err != nil {
perror(err, "destroy state entry")
@ -220,7 +220,7 @@ func (k *outcome) main(msg message.Msg) {
ms.fatal("cannot commit system setup:", err)
}
ms.uintptr |= mainNeedsRevert
ms.store = state.NewMulti(msg, k.state.sc.RunDirPath)
ms.store = store.NewMulti(msg, k.state.sc.RunDirPath)
ctx, cancel := context.WithCancel(k.ctx)
defer cancel()
@ -281,7 +281,7 @@ func (k *outcome) main(msg message.Msg) {
}
// shim accepted setup payload, create process state
if ok, err := ms.store.Do(k.state.identity.unwrap(), func(c state.Cursor) {
if ok, err := ms.store.Do(k.state.identity.unwrap(), func(c store.Cursor) {
if err := c.Save(&hst.State{
ID: k.state.id.unwrap(),
PID: os.Getpid(),

View File

@ -1,64 +0,0 @@
package state
import (
"errors"
"maps"
"hakurei.app/hst"
)
var (
ErrDuplicate = errors.New("store contains duplicates")
)
/*
Joiner is the interface that wraps the Join method.
The Join function uses Joiner if available.
*/
type Joiner interface {
Join() (map[hst.ID]*hst.State, error)
}
// Join returns joined state entries of all active identities.
func Join(s Store) (map[hst.ID]*hst.State, error) {
if j, ok := s.(Joiner); ok {
return j.Join()
}
var (
aids []int
entries = make(map[hst.ID]*hst.State)
el int
res map[hst.ID]*hst.State
loadErr error
)
if ln, err := s.List(); err != nil {
return nil, err
} else {
aids = ln
}
for _, aid := range aids {
if _, err := s.Do(aid, func(c Cursor) {
res, loadErr = c.Load()
}); err != nil {
return nil, err
}
if loadErr != nil {
return nil, loadErr
}
// save expected length
el = len(entries) + len(res)
maps.Copy(entries, res)
if len(entries) != el {
return nil, ErrDuplicate
}
}
return entries, nil
}

View File

@ -1,7 +1,8 @@
// Package state provides cross-process state tracking for hakurei container instances.
package state
package store
import (
"errors"
"maps"
"strconv"
"hakurei.app/container/check"
@ -11,7 +12,7 @@ import (
/* this provides an implementation of Store on top of the improved state tracking to ease in the changes */
type Store interface {
type Compat interface {
// Do calls f exactly once and ensures store exclusivity until f returns.
// Returns whether f is called and any errors during the locking process.
// Cursor provided to f becomes invalid as soon as f returns.
@ -59,7 +60,7 @@ func (s storeAdapter) List() ([]int, error) {
}
// NewMulti returns an instance of the multi-file store.
func NewMulti(msg message.Msg, prefix *check.Absolute) Store {
func NewMulti(msg message.Msg, prefix *check.Absolute) Compat {
return storeAdapter{msg, newStore(prefix.Append("state"))}
}
@ -129,3 +130,57 @@ func (h *storeHandle) Len() (int, error) {
}
return n, err
}
var (
ErrDuplicate = errors.New("store contains duplicates")
)
// Joiner is the interface that wraps the Join method.
//
// The Join function uses Joiner if available.
type Joiner interface {
Join() (map[hst.ID]*hst.State, error)
}
// Join returns joined state entries of all active identities.
func Join(s Compat) (map[hst.ID]*hst.State, error) {
if j, ok := s.(Joiner); ok {
return j.Join()
}
var (
aids []int
entries = make(map[hst.ID]*hst.State)
el int
res map[hst.ID]*hst.State
loadErr error
)
if ln, err := s.List(); err != nil {
return nil, err
} else {
aids = ln
}
for _, aid := range aids {
if _, err := s.Do(aid, func(c Cursor) {
res, loadErr = c.Load()
}); err != nil {
return nil, err
}
if loadErr != nil {
return nil, loadErr
}
// save expected length
el = len(entries) + len(res)
maps.Copy(entries, res)
if len(entries) != el {
return nil, ErrDuplicate
}
}
return entries, nil
}

View File

@ -1,4 +1,4 @@
package state_test
package store_test
import (
"log"
@ -10,12 +10,12 @@ import (
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/internal/state"
"hakurei.app/internal/store"
"hakurei.app/message"
)
func TestMulti(t *testing.T) {
s := state.NewMulti(message.NewMsg(log.New(log.Writer(), "multi: ", 0)), check.MustAbs(t.TempDir()))
s := store.NewMulti(message.NewMsg(log.New(log.Writer(), "multi: ", 0)), check.MustAbs(t.TempDir()))
t.Run("list empty store", func(t *testing.T) {
if identities, err := s.List(); err != nil {
@ -43,14 +43,14 @@ func TestMulti(t *testing.T) {
tc[i].Time = time.Now()
}
do := func(identity int, f func(c state.Cursor)) {
do := func(identity int, f func(c store.Cursor)) {
if ok, err := s.Do(identity, f); err != nil {
t.Fatalf("Do: ok = %v, error = %v", ok, err)
}
}
insert := func(i, identity int) {
do(identity, func(c state.Cursor) {
do(identity, func(c store.Cursor) {
if err := c.Save(&tc[i]); err != nil {
t.Fatalf("Save: error = %v", err)
}
@ -58,7 +58,7 @@ func TestMulti(t *testing.T) {
}
check := func(i, identity int) {
do(identity, func(c state.Cursor) {
do(identity, func(c store.Cursor) {
if entries, err := c.Load(); err != nil {
t.Fatalf("Load: error = %v", err)
} else if got, ok := entries[tc[i].ID]; !ok {
@ -98,19 +98,19 @@ func TestMulti(t *testing.T) {
}
// join store
if entries, err := state.Join(s); err != nil {
if entries, err := store.Join(s); err != nil {
t.Fatalf("Join: error = %v", err)
} else if len(entries) != 3 {
t.Fatalf("Join(s) = %#v", entries)
}
// clear identity 1
do(1, func(c state.Cursor) {
do(1, func(c store.Cursor) {
if err := c.Destroy(tc[insertEntryOtherApp].ID); err != nil {
t.Fatalf("Destroy: error = %v", err)
}
})
do(1, func(c state.Cursor) {
do(1, func(c store.Cursor) {
if l, err := c.Len(); err != nil {
t.Fatalf("Len: error = %v", err)
} else if l != 0 {

View File

@ -1,4 +1,4 @@
package state
package store
import (
"encoding/gob"

View File

@ -1,4 +1,4 @@
package state
package store
import (
"bytes"

View File

@ -1,4 +1,4 @@
package state
package store
import (
"encoding/hex"

View File

@ -1,4 +1,4 @@
package state
package store
import (
"bytes"

View File

@ -1,4 +1,4 @@
package state
package store
import (
"errors"

View File

@ -1,4 +1,4 @@
package state
package store
import (
"errors"

View File

@ -1,4 +1,4 @@
package state
package store
import (
"errors"

View File

@ -1,4 +1,4 @@
package state
package store
import (
"cmp"