internal/app/spcontainer: check params init behaviour
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Hakurei (push) Successful in 3m6s
Test / Hpkg (push) Successful in 4m4s
Test / Sandbox (push) Successful in 1m21s
Test / Hakurei (race detector) (push) Successful in 5m23s
Test / Sandbox (race detector) (push) Successful in 2m8s
Test / Flake checks (push) Successful in 1m31s
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Hakurei (push) Successful in 3m6s
Test / Hpkg (push) Successful in 4m4s
Test / Sandbox (push) Successful in 1m21s
Test / Hakurei (race detector) (push) Successful in 5m23s
Test / Sandbox (race detector) (push) Successful in 2m8s
Test / Flake checks (push) Successful in 1m31s
This change also significantly reduces duplicate information in test case. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
9290748761
commit
9e3df0905b
@ -33,19 +33,17 @@ var checkExpectInstanceId = *(*state.ID)(bytes.Repeat([]byte{0xaa}, len(state.ID
|
|||||||
|
|
||||||
type opBehaviourTestCase struct {
|
type opBehaviourTestCase struct {
|
||||||
name string
|
name string
|
||||||
newOp func() outcomeOp
|
newOp func(isShim, clearUnexported bool) outcomeOp
|
||||||
newConfig func() *hst.Config
|
newConfig func() *hst.Config
|
||||||
|
|
||||||
pStateSys func(state *outcomeStateSys)
|
pStateSys func(state *outcomeStateSys)
|
||||||
toSystem []stub.Call
|
toSystem []stub.Call
|
||||||
wantOpSys outcomeOp
|
|
||||||
wantSys *system.I
|
wantSys *system.I
|
||||||
extraCheckSys func(t *testing.T, state *outcomeStateSys)
|
extraCheckSys func(t *testing.T, state *outcomeStateSys)
|
||||||
wantErrSystem error
|
wantErrSystem error
|
||||||
|
|
||||||
pStateContainer func(state *outcomeStateParams)
|
pStateContainer func(state *outcomeStateParams)
|
||||||
toContainer []stub.Call
|
toContainer []stub.Call
|
||||||
wantOpContainer outcomeOp
|
|
||||||
wantParams *container.Params
|
wantParams *container.Params
|
||||||
extraCheckParams func(t *testing.T, state *outcomeStateParams)
|
extraCheckParams func(t *testing.T, state *outcomeStateParams)
|
||||||
wantErrContainer error
|
wantErrContainer error
|
||||||
@ -94,11 +92,11 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
|||||||
if err := s.populateLocal(k, k); err != nil {
|
if err := s.populateLocal(k, k); err != nil {
|
||||||
t.Fatalf("populateLocal: error = %v", err)
|
t.Fatalf("populateLocal: error = %v", err)
|
||||||
}
|
}
|
||||||
stateSys := s.newSys(config, system.New(panicMsgContext{}, panicMsgContext{}, checkExpectUid))
|
stateSys := s.newSys(config, newI())
|
||||||
if tc.pStateSys != nil {
|
if tc.pStateSys != nil {
|
||||||
tc.pStateSys(stateSys)
|
tc.pStateSys(stateSys)
|
||||||
}
|
}
|
||||||
op := tc.newOp()
|
op := tc.newOp(false, true)
|
||||||
|
|
||||||
if err := op.toSystem(stateSys); !reflect.DeepEqual(err, tc.wantErrSystem) {
|
if err := op.toSystem(stateSys); !reflect.DeepEqual(err, tc.wantErrSystem) {
|
||||||
t.Errorf("toSystem: error = %v, want %v", err, tc.wantErrSystem)
|
t.Errorf("toSystem: error = %v, want %v", err, tc.wantErrSystem)
|
||||||
@ -118,8 +116,8 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
|||||||
if tc.extraCheckSys != nil {
|
if tc.extraCheckSys != nil {
|
||||||
tc.extraCheckSys(t, stateSys)
|
tc.extraCheckSys(t, stateSys)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(op, tc.wantOpSys) {
|
if wantOpSys := tc.newOp(true, false); !reflect.DeepEqual(op, wantOpSys) {
|
||||||
t.Errorf("toSystem: op = %#v, want %#v", op, tc.wantOpSys)
|
t.Errorf("toSystem: op = %#v, want %#v", op, wantOpSys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +131,7 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
|||||||
if tc.pStateContainer != nil {
|
if tc.pStateContainer != nil {
|
||||||
tc.pStateContainer(stateParams)
|
tc.pStateContainer(stateParams)
|
||||||
}
|
}
|
||||||
op := tc.newOp()
|
op := tc.newOp(true, true)
|
||||||
|
|
||||||
if err := op.toContainer(stateParams); !reflect.DeepEqual(err, tc.wantErrContainer) {
|
if err := op.toContainer(stateParams); !reflect.DeepEqual(err, tc.wantErrContainer) {
|
||||||
t.Errorf("toContainer: error = %v, want %v", err, tc.wantErrContainer)
|
t.Errorf("toContainer: error = %v, want %v", err, tc.wantErrContainer)
|
||||||
@ -144,14 +142,11 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(stateParams.params, tc.wantParams) {
|
if !reflect.DeepEqual(stateParams.params, tc.wantParams) {
|
||||||
t.Errorf("toContainer: %#v, want %#v", stateParams.params, tc.wantParams)
|
t.Errorf("toContainer:\n%s\nwant\n%s", mustMarshal(stateParams.params), mustMarshal(tc.wantParams))
|
||||||
}
|
}
|
||||||
if tc.extraCheckParams != nil {
|
if tc.extraCheckParams != nil {
|
||||||
tc.extraCheckParams(t, stateParams)
|
tc.extraCheckParams(t, stateParams)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(op, tc.wantOpContainer) {
|
|
||||||
t.Errorf("toContainer: op = %#v, want %#v", op, tc.wantOpContainer)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@ -167,6 +162,8 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newI() *system.I { return system.New(panicMsgContext{}, panicMsgContext{}, checkExpectUid) }
|
||||||
|
|
||||||
type kstub struct {
|
type kstub struct {
|
||||||
panicDispatcher
|
panicDispatcher
|
||||||
*stub.Stub[syscallDispatcher]
|
*stub.Stub[syscallDispatcher]
|
||||||
|
@ -9,45 +9,43 @@ import (
|
|||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/message"
|
|
||||||
"hakurei.app/system"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpAccountOp(t *testing.T) {
|
func TestSpAccountOp(t *testing.T) {
|
||||||
config := hst.Template()
|
config := hst.Template()
|
||||||
|
|
||||||
checkOpBehaviour(t, []opBehaviourTestCase{
|
checkOpBehaviour(t, []opBehaviourTestCase{
|
||||||
{"invalid state", func() outcomeOp { return spAccountOp{} }, func() *hst.Config {
|
{"invalid state", func(bool, bool) outcomeOp { return spAccountOp{} }, func() *hst.Config {
|
||||||
c := hst.Template()
|
c := hst.Template()
|
||||||
c.Container.Shell = nil
|
c.Container.Shell = nil
|
||||||
return c
|
return c
|
||||||
}, nil, []stub.Call{
|
}, nil, []stub.Call{
|
||||||
// this op performs basic validation and does not make calls during toSystem
|
// this op performs basic validation and does not make calls during toSystem
|
||||||
}, spAccountOp{}, system.New(t.Context(), message.NewMsg(nil), checkExpectUid), nil, syscall.ENOTRECOVERABLE, nil, nil, nil, nil, nil, nil},
|
}, nil, nil, syscall.ENOTRECOVERABLE, nil, nil, nil, nil, nil},
|
||||||
|
|
||||||
{"invalid user name", func() outcomeOp { return spAccountOp{} }, func() *hst.Config {
|
{"invalid user name", func(bool, bool) outcomeOp { return spAccountOp{} }, func() *hst.Config {
|
||||||
c := hst.Template()
|
c := hst.Template()
|
||||||
c.Container.Username = "9"
|
c.Container.Username = "9"
|
||||||
return c
|
return c
|
||||||
}, nil, []stub.Call{
|
}, nil, []stub.Call{
|
||||||
// this op performs basic validation and does not make calls during toSystem
|
// this op performs basic validation and does not make calls during toSystem
|
||||||
}, spAccountOp{}, nil, nil, &hst.AppError{
|
}, nil, nil, &hst.AppError{
|
||||||
Step: "finalise",
|
Step: "finalise",
|
||||||
Err: os.ErrInvalid,
|
Err: os.ErrInvalid,
|
||||||
Msg: `invalid user name "9"`,
|
Msg: `invalid user name "9"`,
|
||||||
}, nil, nil, nil, nil, nil, nil},
|
}, nil, nil, nil, nil, nil},
|
||||||
|
|
||||||
{"success fallback username", func() outcomeOp { return spAccountOp{} }, func() *hst.Config {
|
{"success fallback username", func(bool, bool) outcomeOp { return spAccountOp{} }, func() *hst.Config {
|
||||||
c := hst.Template()
|
c := hst.Template()
|
||||||
c.Container.Username = ""
|
c.Container.Username = ""
|
||||||
return c
|
return c
|
||||||
}, nil, []stub.Call{
|
}, nil, []stub.Call{
|
||||||
// this op performs basic validation and does not make calls during toSystem
|
// this op performs basic validation and does not make calls during toSystem
|
||||||
}, spAccountOp{}, system.New(t.Context(), message.NewMsg(nil), checkExpectUid), nil, nil, func(state *outcomeStateParams) {
|
}, newI(), nil, nil, func(state *outcomeStateParams) {
|
||||||
state.params.Ops = new(container.Ops)
|
state.params.Ops = new(container.Ops)
|
||||||
}, []stub.Call{
|
}, []stub.Call{
|
||||||
// this op configures the container state and does not make calls during toContainer
|
// this op configures the container state and does not make calls during toContainer
|
||||||
}, spAccountOp{}, &container.Params{
|
}, &container.Params{
|
||||||
Dir: config.Container.Home,
|
Dir: config.Container.Home,
|
||||||
Ops: new(container.Ops).
|
Ops: new(container.Ops).
|
||||||
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
|
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
|
||||||
@ -64,13 +62,13 @@ func TestSpAccountOp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}, nil},
|
}, nil},
|
||||||
|
|
||||||
{"success", func() outcomeOp { return spAccountOp{} }, hst.Template, nil, []stub.Call{
|
{"success", func(bool, bool) outcomeOp { return spAccountOp{} }, hst.Template, nil, []stub.Call{
|
||||||
// this op performs basic validation and does not make calls during toSystem
|
// this op performs basic validation and does not make calls during toSystem
|
||||||
}, spAccountOp{}, system.New(t.Context(), message.NewMsg(nil), checkExpectUid), nil, nil, func(state *outcomeStateParams) {
|
}, newI(), nil, nil, func(state *outcomeStateParams) {
|
||||||
state.params.Ops = new(container.Ops)
|
state.params.Ops = new(container.Ops)
|
||||||
}, []stub.Call{
|
}, []stub.Call{
|
||||||
// this op configures the container state and does not make calls during toContainer
|
// this op configures the container state and does not make calls during toContainer
|
||||||
}, spAccountOp{}, &container.Params{
|
}, &container.Params{
|
||||||
Dir: config.Container.Home,
|
Dir: config.Container.Home,
|
||||||
Ops: new(container.Ops).
|
Ops: new(container.Ops).
|
||||||
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
|
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
|
||||||
|
140
internal/app/spcontainer_test.go
Normal file
140
internal/app/spcontainer_test.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maps"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"hakurei.app/container"
|
||||||
|
"hakurei.app/container/bits"
|
||||||
|
"hakurei.app/container/fhs"
|
||||||
|
"hakurei.app/container/seccomp"
|
||||||
|
"hakurei.app/container/stub"
|
||||||
|
"hakurei.app/hst"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSpParamsOp(t *testing.T) {
|
||||||
|
config := hst.Template()
|
||||||
|
|
||||||
|
checkOpBehaviour(t, []opBehaviourTestCase{
|
||||||
|
{"invalid program path", func(isShim, _ bool) outcomeOp {
|
||||||
|
if !isShim {
|
||||||
|
return new(spParamsOp)
|
||||||
|
}
|
||||||
|
return &spParamsOp{Term: "xterm", TermSet: true}
|
||||||
|
}, func() *hst.Config {
|
||||||
|
c := hst.Template()
|
||||||
|
c.Container.Path = nil
|
||||||
|
return c
|
||||||
|
}, nil, []stub.Call{
|
||||||
|
call("lookupEnv", stub.ExpectArgs{"TERM"}, "xterm", nil),
|
||||||
|
}, newI().
|
||||||
|
Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711), nil, nil, nil, []stub.Call{
|
||||||
|
// this op configures the container state and does not make calls during toContainer
|
||||||
|
}, nil, nil, &hst.AppError{
|
||||||
|
Step: "finalise",
|
||||||
|
Err: os.ErrInvalid,
|
||||||
|
Msg: "invalid program path",
|
||||||
|
}},
|
||||||
|
|
||||||
|
{"success defaultargs secure", func(isShim, _ bool) outcomeOp {
|
||||||
|
if !isShim {
|
||||||
|
return new(spParamsOp)
|
||||||
|
}
|
||||||
|
return &spParamsOp{Term: "xterm", TermSet: true}
|
||||||
|
}, func() *hst.Config {
|
||||||
|
c := hst.Template()
|
||||||
|
c.Container.Args = nil
|
||||||
|
c.Container.Multiarch = false
|
||||||
|
c.Container.SeccompCompat = false
|
||||||
|
c.Container.Devel = false
|
||||||
|
c.Container.Userns = false
|
||||||
|
c.Container.Tty = false
|
||||||
|
c.Container.Device = false
|
||||||
|
return c
|
||||||
|
}, nil, []stub.Call{
|
||||||
|
call("lookupEnv", stub.ExpectArgs{"TERM"}, "xterm", nil),
|
||||||
|
}, newI().
|
||||||
|
Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711), nil, nil, nil, []stub.Call{
|
||||||
|
// this op configures the container state and does not make calls during toContainer
|
||||||
|
}, &container.Params{
|
||||||
|
Hostname: config.Container.Hostname,
|
||||||
|
HostNet: config.Container.HostNet,
|
||||||
|
HostAbstract: config.Container.HostAbstract,
|
||||||
|
Path: config.Container.Path,
|
||||||
|
Args: []string{config.Container.Path.String()},
|
||||||
|
SeccompPresets: bits.PresetExt | bits.PresetDenyDevel | bits.PresetDenyNS | bits.PresetDenyTTY,
|
||||||
|
Uid: 1000,
|
||||||
|
Gid: 100,
|
||||||
|
Ops: new(container.Ops).
|
||||||
|
Root(m("/var/lib/hakurei/base/org.debian"), bits.BindWritable).
|
||||||
|
Proc(fhs.AbsProc).Tmpfs(hst.AbsTmp, 1<<12, 0755).
|
||||||
|
DevWritable(fhs.AbsDev, true).
|
||||||
|
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
|
||||||
|
}, func(t *testing.T, state *outcomeStateParams) {
|
||||||
|
wantEnv := map[string]string{
|
||||||
|
"TERM": "xterm",
|
||||||
|
}
|
||||||
|
maps.Copy(wantEnv, config.Container.Env)
|
||||||
|
if !maps.Equal(state.env, wantEnv) {
|
||||||
|
t.Errorf("toContainer: env = %#v, want %#v", state.env, wantEnv)
|
||||||
|
}
|
||||||
|
|
||||||
|
const wantAutoEtcPrefix = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||||
|
if state.as.AutoEtcPrefix != wantAutoEtcPrefix {
|
||||||
|
t.Errorf("toContainer: as.AutoEtcPrefix = %q, want %q", state.as.AutoEtcPrefix, wantAutoEtcPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantFilesystems := config.Container.Filesystem[1:]
|
||||||
|
if !reflect.DeepEqual(state.filesystem, wantFilesystems) {
|
||||||
|
t.Errorf("toContainer: filesystem = %#v, want %#v", state.filesystem, wantFilesystems)
|
||||||
|
}
|
||||||
|
}, nil},
|
||||||
|
|
||||||
|
{"success", func(isShim, _ bool) outcomeOp {
|
||||||
|
if !isShim {
|
||||||
|
return new(spParamsOp)
|
||||||
|
}
|
||||||
|
return &spParamsOp{Term: "xterm", TermSet: true}
|
||||||
|
}, hst.Template, nil, []stub.Call{
|
||||||
|
call("lookupEnv", stub.ExpectArgs{"TERM"}, "xterm", nil),
|
||||||
|
}, newI().
|
||||||
|
Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711), nil, nil, nil, []stub.Call{
|
||||||
|
// this op configures the container state and does not make calls during toContainer
|
||||||
|
}, &container.Params{
|
||||||
|
Hostname: config.Container.Hostname,
|
||||||
|
RetainSession: config.Container.Tty,
|
||||||
|
HostNet: config.Container.HostNet,
|
||||||
|
HostAbstract: config.Container.HostAbstract,
|
||||||
|
Path: config.Container.Path,
|
||||||
|
Args: config.Container.Args,
|
||||||
|
SeccompFlags: seccomp.AllowMultiarch,
|
||||||
|
Uid: 1000,
|
||||||
|
Gid: 100,
|
||||||
|
Ops: new(container.Ops).
|
||||||
|
Root(m("/var/lib/hakurei/base/org.debian"), bits.BindWritable).
|
||||||
|
Proc(fhs.AbsProc).Tmpfs(hst.AbsTmp, 1<<12, 0755).
|
||||||
|
Bind(fhs.AbsDev, fhs.AbsDev, bits.BindWritable|bits.BindDevice).
|
||||||
|
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
|
||||||
|
}, func(t *testing.T, state *outcomeStateParams) {
|
||||||
|
wantEnv := map[string]string{
|
||||||
|
"TERM": "xterm",
|
||||||
|
}
|
||||||
|
maps.Copy(wantEnv, config.Container.Env)
|
||||||
|
if !maps.Equal(state.env, wantEnv) {
|
||||||
|
t.Errorf("toContainer: env = %#v, want %#v", state.env, wantEnv)
|
||||||
|
}
|
||||||
|
|
||||||
|
const wantAutoEtcPrefix = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||||
|
if state.as.AutoEtcPrefix != wantAutoEtcPrefix {
|
||||||
|
t.Errorf("toContainer: as.AutoEtcPrefix = %q, want %q", state.as.AutoEtcPrefix, wantAutoEtcPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantFilesystems := config.Container.Filesystem[1:]
|
||||||
|
if !reflect.DeepEqual(state.filesystem, wantFilesystems) {
|
||||||
|
t.Errorf("toContainer: filesystem = %#v, want %#v", state.filesystem, wantFilesystems)
|
||||||
|
}
|
||||||
|
}, nil},
|
||||||
|
})
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user