hst: replace internal/app error
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Hpkg (push) Successful in 4m3s
Test / Sandbox (race detector) (push) Successful in 4m36s
Test / Hakurei (race detector) (push) Successful in 5m17s
Test / Sandbox (push) Successful in 1m27s
Test / Hakurei (push) Successful in 2m15s
Test / Flake checks (push) Successful in 1m28s
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Hpkg (push) Successful in 4m3s
Test / Sandbox (race detector) (push) Successful in 4m36s
Test / Hakurei (race detector) (push) Successful in 5m17s
Test / Sandbox (push) Successful in 1m27s
Test / Hakurei (push) Successful in 2m15s
Test / Flake checks (push) Successful in 1m28s
This turns out to still be quite useful across internal/app and its relatives. Perhaps a cleaner replacement for baseError. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
1cdc6b4246
commit
8690419c2d
30
hst/hst.go
30
hst/hst.go
@ -2,12 +2,42 @@
|
|||||||
package hst
|
package hst
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/system"
|
"hakurei.app/system"
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/system/dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// An AppError is returned while starting an app according to [hst.Config].
|
||||||
|
type AppError struct {
|
||||||
|
Step string
|
||||||
|
Err error
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *AppError) Error() string { return e.Err.Error() }
|
||||||
|
func (e *AppError) Unwrap() error { return e.Err }
|
||||||
|
func (e *AppError) Message() string {
|
||||||
|
if e.Msg != "" {
|
||||||
|
return e.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case errors.As(e.Err, new(*os.PathError)),
|
||||||
|
errors.As(e.Err, new(*os.LinkError)),
|
||||||
|
errors.As(e.Err, new(*os.SyscallError)),
|
||||||
|
errors.As(e.Err, new(*net.OpError)):
|
||||||
|
return "cannot " + e.Error()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "cannot " + e.Step + ": " + e.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Paths contains environment-dependent paths used by hakurei.
|
// Paths contains environment-dependent paths used by hakurei.
|
||||||
type Paths struct {
|
type Paths struct {
|
||||||
// temporary directory returned by [os.TempDir] (usually `/tmp`)
|
// temporary directory returned by [os.TempDir] (usually `/tmp`)
|
||||||
|
@ -2,11 +2,93 @@ package hst_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"hakurei.app/container"
|
||||||
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestAppError(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
s string
|
||||||
|
message string
|
||||||
|
is, isF error
|
||||||
|
}{
|
||||||
|
{"message", &hst.AppError{Step: "obtain uid from hsu", Err: stub.UniqueError(0),
|
||||||
|
Msg: "the setuid helper is missing: /run/wrappers/bin/hsu"},
|
||||||
|
"unique error 0 injected by the test suite",
|
||||||
|
"the setuid helper is missing: /run/wrappers/bin/hsu",
|
||||||
|
stub.UniqueError(0), os.ErrNotExist},
|
||||||
|
|
||||||
|
{"os.PathError", &hst.AppError{Step: "passthrough os.PathError",
|
||||||
|
Err: &os.PathError{Op: "stat", Path: "/proc/nonexistent", Err: os.ErrNotExist}},
|
||||||
|
"stat /proc/nonexistent: file does not exist",
|
||||||
|
"cannot stat /proc/nonexistent: file does not exist",
|
||||||
|
os.ErrNotExist, stub.UniqueError(0xdeadbeef)},
|
||||||
|
|
||||||
|
{"os.LinkError", &hst.AppError{Step: "passthrough os.LinkError",
|
||||||
|
Err: &os.LinkError{Op: "link", Old: "/proc/self", New: "/proc/nonexistent", Err: os.ErrNotExist}},
|
||||||
|
"link /proc/self /proc/nonexistent: file does not exist",
|
||||||
|
"cannot link /proc/self /proc/nonexistent: file does not exist",
|
||||||
|
os.ErrNotExist, stub.UniqueError(0xdeadbeef)},
|
||||||
|
|
||||||
|
{"os.SyscallError", &hst.AppError{Step: "passthrough os.SyscallError",
|
||||||
|
Err: &os.SyscallError{Syscall: "meow", Err: syscall.ENOSYS}},
|
||||||
|
"meow: function not implemented",
|
||||||
|
"cannot meow: function not implemented",
|
||||||
|
syscall.ENOSYS, syscall.ENOTRECOVERABLE},
|
||||||
|
|
||||||
|
{"net.OpError", &hst.AppError{Step: "passthrough net.OpError",
|
||||||
|
Err: &net.OpError{Op: "dial", Net: "cat", Err: net.UnknownNetworkError("cat")}},
|
||||||
|
"dial cat: unknown network cat",
|
||||||
|
"cannot dial cat: unknown network cat",
|
||||||
|
net.UnknownNetworkError("cat"), syscall.ENOTRECOVERABLE},
|
||||||
|
|
||||||
|
{"default", &hst.AppError{Step: "initialise container configuration", Err: stub.UniqueError(1)},
|
||||||
|
"unique error 1 injected by the test suite",
|
||||||
|
"cannot initialise container configuration: unique error 1 injected by the test suite",
|
||||||
|
stub.UniqueError(1), os.ErrInvalid},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Run("error", func(t *testing.T) {
|
||||||
|
if got := tc.err.Error(); got != tc.s {
|
||||||
|
t.Errorf("Error: %s, want %s", got, tc.s)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("message", func(t *testing.T) {
|
||||||
|
gotMessage, gotMessageOk := container.GetErrorMessage(tc.err)
|
||||||
|
if want := tc.message != "\x00"; gotMessageOk != want {
|
||||||
|
t.Errorf("GetErrorMessage: ok = %v, want %v", gotMessage, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotMessageOk {
|
||||||
|
if gotMessage != tc.message {
|
||||||
|
t.Errorf("GetErrorMessage: %s, want %s", gotMessage, tc.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("is", func(t *testing.T) {
|
||||||
|
if !errors.Is(tc.err, tc.is) {
|
||||||
|
t.Errorf("Is: unexpected false for %v", tc.is)
|
||||||
|
}
|
||||||
|
if errors.Is(tc.err, tc.isF) {
|
||||||
|
t.Errorf("Is: unexpected true for %v", tc.isF)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTemplate(t *testing.T) {
|
func TestTemplate(t *testing.T) {
|
||||||
const want = `{
|
const want = `{
|
||||||
"id": "org.chromium.Chromium",
|
"id": "org.chromium.Chromium",
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal/hlog"
|
"hakurei.app/internal/hlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ func PrintRunStateErr(rs *RunState, runErr error) (code int) {
|
|||||||
} else {
|
} else {
|
||||||
// InnerErr is returned by c.Save(&sd, seal.ct), and are always unwrapped
|
// InnerErr is returned by c.Save(&sd, seal.ct), and are always unwrapped
|
||||||
printMessageError("error returned during revert:",
|
printMessageError("error returned during revert:",
|
||||||
&FinaliseError{Step: "save process state", Err: se.InnerErr})
|
&hst.AppError{Step: "save process state", Err: se.InnerErr})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +134,7 @@ func (e *StateStoreError) equiv(step string) error {
|
|||||||
if e.Inner && e.InnerErr == nil && e.DoErr == nil && e.OpErr == nil && errors.Join(e.Errs...) == nil {
|
if e.Inner && e.InnerErr == nil && e.DoErr == nil && e.OpErr == nil && errors.Join(e.Errs...) == nil {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return &FinaliseError{Step: step, Err: e}
|
return &hst.AppError{Step: step, Err: e}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal"
|
"hakurei.app/internal"
|
||||||
"hakurei.app/internal/app/state"
|
"hakurei.app/internal/app/state"
|
||||||
"hakurei.app/internal/hlog"
|
"hakurei.app/internal/hlog"
|
||||||
@ -119,7 +120,7 @@ func (seal *Outcome) Run(rs *RunState) error {
|
|||||||
|
|
||||||
var e *gob.Encoder
|
var e *gob.Encoder
|
||||||
if fd, encoder, err := container.Setup(&cmd.ExtraFiles); err != nil {
|
if fd, encoder, err := container.Setup(&cmd.ExtraFiles); err != nil {
|
||||||
return &FinaliseError{Step: "create shim setup pipe", Err: err}
|
return &hst.AppError{Step: "create shim setup pipe", Err: err}
|
||||||
} else {
|
} else {
|
||||||
e = encoder
|
e = encoder
|
||||||
cmd.Env = []string{
|
cmd.Env = []string{
|
||||||
@ -139,7 +140,7 @@ func (seal *Outcome) Run(rs *RunState) error {
|
|||||||
hlog.Verbosef("setuid helper at %s", hsuPath)
|
hlog.Verbosef("setuid helper at %s", hsuPath)
|
||||||
hlog.Suspend()
|
hlog.Suspend()
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
return &FinaliseError{Step: "start setuid wrapper", Err: err}
|
return &hst.AppError{Step: "start setuid wrapper", Err: err}
|
||||||
}
|
}
|
||||||
rs.setStart()
|
rs.setStart()
|
||||||
|
|
||||||
@ -159,7 +160,7 @@ func (seal *Outcome) Run(rs *RunState) error {
|
|||||||
case err := <-setupErr:
|
case err := <-setupErr:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hlog.Resume()
|
hlog.Resume()
|
||||||
return &FinaliseError{Step: "transmit shim config", Err: err}
|
return &hst.AppError{Step: "transmit shim config", Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -28,35 +27,9 @@ import (
|
|||||||
"hakurei.app/system/wayland"
|
"hakurei.app/system/wayland"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A FinaliseError is returned while finalising a [hst.Config] outcome.
|
|
||||||
type FinaliseError struct {
|
|
||||||
Step string
|
|
||||||
Err error
|
|
||||||
Msg string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *FinaliseError) Error() string { return e.Err.Error() }
|
|
||||||
func (e *FinaliseError) Unwrap() error { return e.Err }
|
|
||||||
func (e *FinaliseError) Message() string {
|
|
||||||
if e.Msg != "" {
|
|
||||||
return e.Msg
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case errors.As(e.Err, new(*os.PathError)),
|
|
||||||
errors.As(e.Err, new(*os.LinkError)),
|
|
||||||
errors.As(e.Err, new(*os.SyscallError)),
|
|
||||||
errors.As(e.Err, new(*net.OpError)):
|
|
||||||
return "cannot " + e.Error()
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "cannot " + e.Step + ": " + e.Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWithMessage(msg string) error { return newWithMessageError(msg, os.ErrInvalid) }
|
func newWithMessage(msg string) error { return newWithMessageError(msg, os.ErrInvalid) }
|
||||||
func newWithMessageError(msg string, err error) error {
|
func newWithMessageError(msg string, err error) error {
|
||||||
return &FinaliseError{Step: "finalise", Err: err, Msg: msg}
|
return &hst.AppError{Step: "finalise", Err: err, Msg: msg}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Outcome is the runnable state of a hakurei container via [hst.Config].
|
// An Outcome is the runnable state of a hakurei container via [hst.Config].
|
||||||
@ -188,7 +161,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
// encode initial configuration for state tracking
|
// encode initial configuration for state tracking
|
||||||
ct := new(bytes.Buffer)
|
ct := new(bytes.Buffer)
|
||||||
if err := gob.NewEncoder(ct).Encode(config); err != nil {
|
if err := gob.NewEncoder(ct).Encode(config); err != nil {
|
||||||
return &FinaliseError{Step: "encode initial config", Err: err}
|
return &hst.AppError{Step: "encode initial config", Err: err}
|
||||||
}
|
}
|
||||||
seal.ct = ct
|
seal.ct = ct
|
||||||
}
|
}
|
||||||
@ -238,7 +211,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
if config.Path == nil {
|
if config.Path == nil {
|
||||||
if len(config.Args) > 0 {
|
if len(config.Args) > 0 {
|
||||||
if p, err := sys.LookPath(config.Args[0]); err != nil {
|
if p, err := sys.LookPath(config.Args[0]); err != nil {
|
||||||
return &FinaliseError{Step: "look up executable file", Err: err}
|
return &hst.AppError{Step: "look up executable file", Err: err}
|
||||||
} else if config.Path, err = container.NewAbs(p); err != nil {
|
} else if config.Path, err = container.NewAbs(p); err != nil {
|
||||||
return newWithMessageError(err.Error(), err)
|
return newWithMessageError(err.Error(), err)
|
||||||
}
|
}
|
||||||
@ -304,7 +277,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
seal.container, seal.env, err = newContainer(config.Container, sys, seal.id.String(), &uid, &gid)
|
seal.container, seal.env, err = newContainer(config.Container, sys, seal.id.String(), &uid, &gid)
|
||||||
seal.waitDelay = config.Container.WaitDelay
|
seal.waitDelay = config.Container.WaitDelay
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &FinaliseError{Step: "initialise container configuration", Err: err}
|
return &hst.AppError{Step: "initialise container configuration", Err: err}
|
||||||
}
|
}
|
||||||
if len(config.Args) == 0 {
|
if len(config.Args) == 0 {
|
||||||
config.Args = []string{config.Path.String()}
|
config.Args = []string{config.Path.String()}
|
||||||
@ -427,7 +400,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
if socketPath != nil {
|
if socketPath != nil {
|
||||||
if _, err := sys.Stat(socketPath.String()); err != nil {
|
if _, err := sys.Stat(socketPath.String()); err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
return &FinaliseError{Step: fmt.Sprintf("access X11 socket %q", socketPath), Err: err}
|
return &hst.AppError{Step: fmt.Sprintf("access X11 socket %q", socketPath), Err: err}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
seal.sys.UpdatePermType(system.EX11, socketPath.String(), acl.Read, acl.Write, acl.Execute)
|
seal.sys.UpdatePermType(system.EX11, socketPath.String(), acl.Read, acl.Write, acl.Execute)
|
||||||
@ -451,14 +424,14 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
|
|
||||||
if _, err := sys.Stat(pulseRuntimeDir.String()); err != nil {
|
if _, err := sys.Stat(pulseRuntimeDir.String()); err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
return &FinaliseError{Step: fmt.Sprintf("access PulseAudio directory %q", pulseRuntimeDir), Err: err}
|
return &hst.AppError{Step: fmt.Sprintf("access PulseAudio directory %q", pulseRuntimeDir), Err: err}
|
||||||
}
|
}
|
||||||
return newWithMessage(fmt.Sprintf("PulseAudio directory %q not found", pulseRuntimeDir))
|
return newWithMessage(fmt.Sprintf("PulseAudio directory %q not found", pulseRuntimeDir))
|
||||||
}
|
}
|
||||||
|
|
||||||
if s, err := sys.Stat(pulseSocket.String()); err != nil {
|
if s, err := sys.Stat(pulseSocket.String()); err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
return &FinaliseError{Step: fmt.Sprintf("access PulseAudio socket %q", pulseSocket), Err: err}
|
return &hst.AppError{Step: fmt.Sprintf("access PulseAudio socket %q", pulseSocket), Err: err}
|
||||||
}
|
}
|
||||||
return newWithMessage(fmt.Sprintf("PulseAudio directory %q found but socket does not exist", pulseRuntimeDir))
|
return newWithMessage(fmt.Sprintf("PulseAudio directory %q found but socket does not exist", pulseRuntimeDir))
|
||||||
} else {
|
} else {
|
||||||
@ -482,7 +455,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
// from environment
|
// from environment
|
||||||
if p, ok := sys.LookupEnv(pulseCookie); ok {
|
if p, ok := sys.LookupEnv(pulseCookie); ok {
|
||||||
if a, err := container.NewAbs(p); err != nil {
|
if a, err := container.NewAbs(p); err != nil {
|
||||||
return &FinaliseError{Step: paLocateStep, Err: err}
|
return &hst.AppError{Step: paLocateStep, Err: err}
|
||||||
} else {
|
} else {
|
||||||
// this takes precedence, do not verify whether the file is accessible
|
// this takes precedence, do not verify whether the file is accessible
|
||||||
paCookiePath = a
|
paCookiePath = a
|
||||||
@ -493,7 +466,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
// $HOME/.pulse-cookie
|
// $HOME/.pulse-cookie
|
||||||
if p, ok := sys.LookupEnv(home); ok {
|
if p, ok := sys.LookupEnv(home); ok {
|
||||||
if a, err := container.NewAbs(p); err != nil {
|
if a, err := container.NewAbs(p); err != nil {
|
||||||
return &FinaliseError{Step: paLocateStep, Err: err}
|
return &hst.AppError{Step: paLocateStep, Err: err}
|
||||||
} else {
|
} else {
|
||||||
paCookiePath = a.Append(".pulse-cookie")
|
paCookiePath = a.Append(".pulse-cookie")
|
||||||
}
|
}
|
||||||
@ -501,7 +474,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
if s, err := sys.Stat(paCookiePath.String()); err != nil {
|
if s, err := sys.Stat(paCookiePath.String()); err != nil {
|
||||||
paCookiePath = nil
|
paCookiePath = nil
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
return &FinaliseError{Step: "access PulseAudio cookie", Err: err}
|
return &hst.AppError{Step: "access PulseAudio cookie", Err: err}
|
||||||
}
|
}
|
||||||
// fallthrough
|
// fallthrough
|
||||||
} else if s.IsDir() {
|
} else if s.IsDir() {
|
||||||
@ -514,14 +487,14 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
// $XDG_CONFIG_HOME/pulse/cookie
|
// $XDG_CONFIG_HOME/pulse/cookie
|
||||||
if p, ok := sys.LookupEnv(xdgConfigHome); ok {
|
if p, ok := sys.LookupEnv(xdgConfigHome); ok {
|
||||||
if a, err := container.NewAbs(p); err != nil {
|
if a, err := container.NewAbs(p); err != nil {
|
||||||
return &FinaliseError{Step: paLocateStep, Err: err}
|
return &hst.AppError{Step: paLocateStep, Err: err}
|
||||||
} else {
|
} else {
|
||||||
paCookiePath = a.Append("pulse", "cookie")
|
paCookiePath = a.Append("pulse", "cookie")
|
||||||
}
|
}
|
||||||
if s, err := sys.Stat(paCookiePath.String()); err != nil {
|
if s, err := sys.Stat(paCookiePath.String()); err != nil {
|
||||||
paCookiePath = nil
|
paCookiePath = nil
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
return &FinaliseError{Step: "access PulseAudio cookie", Err: err}
|
return &hst.AppError{Step: "access PulseAudio cookie", Err: err}
|
||||||
}
|
}
|
||||||
// fallthrough
|
// fallthrough
|
||||||
} else if s.IsDir() {
|
} else if s.IsDir() {
|
||||||
@ -609,7 +582,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
|
|||||||
seal.container.Env = make([]string, 0, len(seal.env))
|
seal.container.Env = make([]string, 0, len(seal.env))
|
||||||
for k, v := range seal.env {
|
for k, v := range seal.env {
|
||||||
if strings.IndexByte(k, '=') != -1 {
|
if strings.IndexByte(k, '=') != -1 {
|
||||||
return &FinaliseError{Step: "flatten environment", Err: syscall.EINVAL,
|
return &hst.AppError{Step: "flatten environment", Err: syscall.EINVAL,
|
||||||
Msg: fmt.Sprintf("invalid environment variable %s", k)}
|
Msg: fmt.Sprintf("invalid environment variable %s", k)}
|
||||||
}
|
}
|
||||||
seal.container.Env = append(seal.container.Env, k+"="+v)
|
seal.container.Env = append(seal.container.Env, k+"="+v)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user