summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/decoder.go10
-rw-r--r--pkg/api/handlers/libpod/play.go10
-rw-r--r--pkg/api/handlers/libpod/secrets.go8
-rw-r--r--pkg/api/server/server.go21
-rw-r--r--pkg/domain/entities/pods.go2
-rw-r--r--pkg/domain/infra/abi/containers.go6
-rw-r--r--pkg/domain/infra/abi/play.go6
-rw-r--r--pkg/domain/infra/abi/system.go44
-rw-r--r--pkg/domain/utils/utils_test.go76
-rw-r--r--pkg/env/env_test.go162
-rw-r--r--pkg/machine/config.go29
-rw-r--r--pkg/machine/e2e/inspect_test.go12
-rw-r--r--pkg/machine/e2e/machine_test.go11
-rw-r--r--pkg/machine/qemu/config_test.go23
-rw-r--r--pkg/machine/qemu/machine.go84
-rw-r--r--pkg/machine/wsl/machine.go273
-rw-r--r--pkg/specgen/generate/pod_create.go2
-rw-r--r--pkg/specgen/podspecgen.go2
-rw-r--r--pkg/systemd/generate/pods.go11
-rw-r--r--pkg/systemd/generate/pods_test.go32
-rw-r--r--pkg/util/utils.go23
21 files changed, 678 insertions, 169 deletions
diff --git a/pkg/api/handlers/decoder.go b/pkg/api/handlers/decoder.go
index 5e8f12960..fbe03d97b 100644
--- a/pkg/api/handlers/decoder.go
+++ b/pkg/api/handlers/decoder.go
@@ -21,6 +21,7 @@ func NewAPIDecoder() *schema.Decoder {
d.RegisterConverter(map[string][]string{}, convertURLValuesString)
d.RegisterConverter(time.Time{}, convertTimeString)
d.RegisterConverter(define.ContainerStatus(0), convertContainerStatusString)
+ d.RegisterConverter(map[string]string{}, convertStringMap)
var Signal syscall.Signal
d.RegisterConverter(Signal, convertSignal)
@@ -48,6 +49,15 @@ func convertURLValuesString(query string) reflect.Value {
return reflect.ValueOf(f)
}
+func convertStringMap(query string) reflect.Value {
+ res := make(map[string]string)
+ err := json.Unmarshal([]byte(query), &res)
+ if err != nil {
+ logrus.Infof("convertStringMap: Failed to Unmarshal %s: %s", query, err.Error())
+ }
+ return reflect.ValueOf(res)
+}
+
func convertContainerStatusString(query string) reflect.Value {
result, err := define.StringToContainerStatus(query)
if err != nil {
diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go
index ca9ada761..b71afc28c 100644
--- a/pkg/api/handlers/libpod/play.go
+++ b/pkg/api/handlers/libpod/play.go
@@ -70,6 +70,16 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
password = authConf.Password
}
+ logDriver := query.LogDriver
+ if logDriver == "" {
+ config, err := runtime.GetConfig()
+ if err != nil {
+ utils.Error(w, http.StatusInternalServerError, err)
+ return
+ }
+ query.LogDriver = config.Containers.LogDriver
+ }
+
containerEngine := abi.ContainerEngine{Libpod: runtime}
options := entities.PlayKubeOptions{
Annotations: query.Annotations,
diff --git a/pkg/api/handlers/libpod/secrets.go b/pkg/api/handlers/libpod/secrets.go
index 8708e630c..3ea2c2ea8 100644
--- a/pkg/api/handlers/libpod/secrets.go
+++ b/pkg/api/handlers/libpod/secrets.go
@@ -1,9 +1,7 @@
package libpod
import (
- "encoding/json"
"net/http"
- "reflect"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/pkg/api/handlers/utils"
@@ -20,12 +18,6 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) {
decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder)
)
- decoder.RegisterConverter(map[string]string{}, func(str string) reflect.Value {
- res := make(map[string]string)
- json.Unmarshal([]byte(str), &res)
- return reflect.ValueOf(res)
- })
-
query := struct {
Name string `schema:"name"`
Driver string `schema:"driver"`
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index a906a01f1..7f5537fb4 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -20,7 +20,6 @@ import (
"github.com/containers/podman/v4/pkg/api/server/idle"
"github.com/containers/podman/v4/pkg/api/types"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/coreos/go-systemd/v22/activation"
"github.com/coreos/go-systemd/v22/daemon"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
@@ -65,25 +64,7 @@ func NewServerWithSettings(runtime *libpod.Runtime, listener net.Listener, opts
}
func newServer(runtime *libpod.Runtime, listener net.Listener, opts entities.ServiceOptions) (*APIServer, error) {
- // If listener not provided try socket activation protocol
- if listener == nil {
- if _, found := os.LookupEnv("LISTEN_PID"); !found {
- return nil, fmt.Errorf("no service listener provided and socket activation protocol is not active")
- }
-
- listeners, err := activation.Listeners()
- if err != nil {
- return nil, fmt.Errorf("cannot retrieve file descriptors from systemd: %w", err)
- }
- if len(listeners) != 1 {
- return nil, fmt.Errorf("wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners))
- }
- listener = listeners[0]
- // note that activation.Listeners() return nil when it cannot listen on the fd (i.e. udp connection)
- if listener == nil {
- return nil, fmt.Errorf("unexpected fd received from systemd: cannot listen on it")
- }
- }
+ logrus.Infof("API service listening on %q. URI: %q", listener.Addr(), runtime.RemoteURI())
if opts.CorsHeaders == "" {
logrus.Debug("CORS Headers were not set")
} else {
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 1e25e0872..a19edcbf0 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -122,6 +122,7 @@ type PodCreateOptions struct {
CreateCommand []string `json:"create_command,omitempty"`
Devices []string `json:"devices,omitempty"`
DeviceReadBPs []string `json:"device_read_bps,omitempty"`
+ ExitPolicy string `json:"exit_policy,omitempty"`
Hostname string `json:"hostname,omitempty"`
Infra bool `json:"infra,omitempty"`
InfraImage string `json:"infra_image,omitempty"`
@@ -319,6 +320,7 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod
}
s.Pid = out
s.Hostname = p.Hostname
+ s.ExitPolicy = p.ExitPolicy
s.Labels = p.Labels
s.Devices = p.Devices
s.SecurityOpt = p.SecurityOpt
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 92e29c822..5ca678d6f 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -641,7 +641,11 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
}
restoreOptions.CheckpointImageID = img.ID()
mountPoint, err := img.Mount(ctx, nil, "")
- defer img.Unmount(true)
+ defer func() {
+ if err := img.Unmount(true); err != nil {
+ logrus.Errorf("Failed to unmount image: %v", err)
+ }
+ }()
if err != nil {
return nil, err
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 0da07bab8..b3ded7db6 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -197,7 +197,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
return nil, errors.Errorf("pod does not have a name")
}
- podOpt := entities.PodCreateOptions{Infra: true, Net: &entities.NetOptions{NoHosts: options.NoHosts}}
+ podOpt := entities.PodCreateOptions{
+ Infra: true,
+ Net: &entities.NetOptions{NoHosts: options.NoHosts},
+ ExitPolicy: string(config.PodExitPolicyStop),
+ }
podOpt, err = kube.ToPodOpt(ctx, podName, podOpt, podYAML)
if err != nil {
return nil, err
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 8e96e4154..17df0e3f8 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -6,6 +6,7 @@ import (
"net/url"
"os"
"os/exec"
+ "path/filepath"
"github.com/containers/common/pkg/cgroups"
"github.com/containers/common/pkg/config"
@@ -27,27 +28,40 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
if err != nil {
return nil, err
}
+ info.Host.RemoteSocket = &define.RemoteSocket{Path: ic.Libpod.RemoteURI()}
- socketPath, err := util.SocketPath()
+ // `podman system connection add` invokes podman via ssh to fill in connection string. Here
+ // we are reporting the default systemd activation socket path as we cannot know if a future
+ // service may be run with another URI.
+ if ic.Libpod.RemoteURI() == "" {
+ xdg := "/run"
+ if path, err := util.GetRuntimeDir(); err != nil {
+ // Info is as good as we can guess...
+ return info, err
+ } else if path != "" {
+ xdg = path
+ }
+
+ uri := url.URL{
+ Scheme: "unix",
+ Path: filepath.Join(xdg, "podman", "podman.sock"),
+ }
+ ic.Libpod.SetRemoteURI(uri.String())
+ info.Host.RemoteSocket.Path = uri.Path
+ }
+
+ uri, err := url.Parse(ic.Libpod.RemoteURI())
if err != nil {
return nil, err
}
- rs := define.RemoteSocket{
- Path: socketPath,
- Exists: false,
- }
- // Check if the socket exists
- if fi, err := os.Stat(socketPath); err == nil {
- if fi.Mode()&os.ModeSocket != 0 {
- rs.Exists = true
- }
+ if uri.Scheme == "unix" {
+ _, err := os.Stat(uri.Path)
+ info.Host.RemoteSocket.Exists = err == nil
+ } else {
+ info.Host.RemoteSocket.Exists = true
}
- // TODO
- // it was suggested future versions of this could perform
- // a ping on the socket for greater confidence the socket is
- // actually active.
- info.Host.RemoteSocket = &rs
+
return info, err
}
diff --git a/pkg/domain/utils/utils_test.go b/pkg/domain/utils/utils_test.go
new file mode 100644
index 000000000..952a4b5be
--- /dev/null
+++ b/pkg/domain/utils/utils_test.go
@@ -0,0 +1,76 @@
+package utils
+
+import (
+ "net/url"
+ "sort"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestToLibpodFilters(t *testing.T) {
+ good := url.Values{}
+ good.Set("apple", "red")
+ good.Set("banana", "yellow")
+ good.Set("pear", "")
+ goodResult := []string{"apple=red", "banana=yellow", "pear="}
+ sort.Strings(goodResult)
+
+ empty := url.Values{}
+ type args struct {
+ f url.Values
+ }
+ tests := []struct {
+ name string
+ args args
+ wantFilters []string
+ }{
+ {
+ name: "GoodURLValue",
+ args: args{
+ f: good,
+ },
+ wantFilters: goodResult,
+ },
+ {
+ name: "Empty",
+ args: args{
+ f: empty,
+ },
+ wantFilters: nil,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.ElementsMatchf(t, ToLibpodFilters(tt.args.f), tt.wantFilters, "ToLibpodFilters() = %v, want %v", ToLibpodFilters(tt.args.f), tt.wantFilters)
+ })
+ }
+}
+
+func TestToURLValues(t *testing.T) {
+ good := url.Values{}
+ good.Set("apple", "red")
+ good.Set("banana", "yellow")
+ good.Set("pear", "")
+ goodResult := []string{"apple=red", "banana=yellow", "pear="}
+
+ type args struct {
+ f []string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantFilters url.Values
+ }{
+ {
+ name: "Good",
+ args: args{goodResult},
+ wantFilters: good,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.EqualValuesf(t, ToURLValues(tt.args.f), tt.wantFilters, "ToURLValues() = %v, want %v", ToURLValues(tt.args.f), tt.wantFilters)
+ })
+ }
+}
diff --git a/pkg/env/env_test.go b/pkg/env/env_test.go
new file mode 100644
index 000000000..c77061ecf
--- /dev/null
+++ b/pkg/env/env_test.go
@@ -0,0 +1,162 @@
+package env
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSlice(t *testing.T) {
+ goodMap := make(map[string]string, 0)
+ goodMap["apple"] = "red"
+ goodMap["banana"] = "yellow"
+ goodMap["pear"] = ""
+ goodResult := []string{"apple=red", "banana=yellow", "pear"}
+ type args struct {
+ m map[string]string
+ }
+ tests := []struct {
+ name string
+ args args
+ want []string
+ }{
+ {
+ name: "Good",
+ args: args{
+ m: goodMap,
+ },
+ want: goodResult,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.ElementsMatchf(t, Slice(tt.args.m), tt.want, "Slice() = %v, want %v", Slice(tt.args.m), tt.want)
+ })
+ }
+}
+
+func TestJoin(t *testing.T) {
+ firstMap := make(map[string]string, 0)
+ firstMap["apple"] = "red"
+ secondMap := make(map[string]string, 0)
+ secondMap["banana"] = "yellow"
+ goodResult := make(map[string]string, 0)
+ goodResult["apple"] = "red"
+ goodResult["banana"] = "yellow"
+ overrideResult := make(map[string]string, 0)
+ overrideResult["apple"] = "green"
+ overrideResult["banana"] = "yellow"
+ overrideMap := make(map[string]string, 0)
+ overrideMap["banana"] = "yellow"
+ overrideMap["apple"] = "green"
+ type args struct {
+ base map[string]string
+ override map[string]string
+ }
+ tests := []struct {
+ name string
+ args args
+ want map[string]string
+ }{
+ {
+ name: "GoodJoin",
+ args: args{
+ base: firstMap,
+ override: secondMap,
+ },
+ want: goodResult,
+ },
+ {
+ name: "GoodOverride",
+ args: args{
+ base: firstMap,
+ override: overrideMap,
+ },
+ want: overrideResult,
+ },
+ {
+ name: "EmptyOverride",
+ args: args{
+ base: firstMap,
+ override: nil,
+ },
+ want: firstMap,
+ },
+ {
+ name: "EmptyBase",
+ args: args{
+ base: nil,
+ override: firstMap,
+ },
+ want: firstMap,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := Join(tt.args.base, tt.args.override)
+ assert.EqualValuesf(t, got, tt.want, "Join() = %v, want %v", got, tt.want)
+ })
+ }
+}
+
+func Test_parseEnv(t *testing.T) {
+ good := make(map[string]string)
+
+ type args struct {
+ env map[string]string
+ line string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ name: "Good",
+ args: args{
+ env: good,
+ line: "apple=red",
+ },
+ wantErr: false,
+ },
+ {
+ name: "GoodNoValue",
+ args: args{
+ env: good,
+ line: "apple=",
+ },
+ wantErr: false,
+ },
+ {
+ name: "GoodNoKeyNoValue",
+ args: args{
+ env: good,
+ line: "=",
+ },
+ wantErr: true,
+ },
+ {
+ name: "BadNoKey",
+ args: args{
+ env: good,
+ line: "=foobar",
+ },
+ wantErr: true,
+ },
+ {
+ name: "BadOnlyDelim",
+ args: args{
+ env: good,
+ line: "=",
+ },
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := parseEnv(tt.args.env, tt.args.line); (err != nil) != tt.wantErr {
+ t.Errorf("parseEnv() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
diff --git a/pkg/machine/config.go b/pkg/machine/config.go
index 9a0ce757a..d34776714 100644
--- a/pkg/machine/config.go
+++ b/pkg/machine/config.go
@@ -52,6 +52,7 @@ type Provider interface {
List(opts ListOptions) ([]*ListResponse, error)
IsValidVMName(name string) (bool, error)
CheckExclusiveActiveVM() (bool, string, error)
+ RemoveAndCleanMachines() error
}
type RemoteConnectionType string
@@ -170,11 +171,11 @@ func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url
// GetDataDir returns the filepath where vm images should
// live for podman-machine.
func GetDataDir(vmType string) (string, error) {
- data, err := homedir.GetDataHome()
+ dataDirPrefix, err := DataDirPrefix()
if err != nil {
return "", err
}
- dataDir := filepath.Join(data, "containers", "podman", "machine", vmType)
+ dataDir := filepath.Join(dataDirPrefix, vmType)
if _, err := os.Stat(dataDir); !os.IsNotExist(err) {
return dataDir, nil
}
@@ -182,14 +183,24 @@ func GetDataDir(vmType string) (string, error) {
return dataDir, mkdirErr
}
+// DataDirPrefix returns the path prefix for all machine data files
+func DataDirPrefix() (string, error) {
+ data, err := homedir.GetDataHome()
+ if err != nil {
+ return "", err
+ }
+ dataDir := filepath.Join(data, "containers", "podman", "machine")
+ return dataDir, nil
+}
+
// GetConfigDir returns the filepath to where configuration
// files for podman-machine should live
func GetConfDir(vmType string) (string, error) {
- conf, err := homedir.GetConfigHome()
+ confDirPrefix, err := ConfDirPrefix()
if err != nil {
return "", err
}
- confDir := filepath.Join(conf, "containers", "podman", "machine", vmType)
+ confDir := filepath.Join(confDirPrefix, vmType)
if _, err := os.Stat(confDir); !os.IsNotExist(err) {
return confDir, nil
}
@@ -197,6 +208,16 @@ func GetConfDir(vmType string) (string, error) {
return confDir, mkdirErr
}
+// ConfDirPrefix returns the path prefix for all machine config files
+func ConfDirPrefix() (string, error) {
+ conf, err := homedir.GetConfigHome()
+ if err != nil {
+ return "", err
+ }
+ confDir := filepath.Join(conf, "containers", "podman", "machine")
+ return confDir, nil
+}
+
// ResourceConfig describes physical attributes of the machine
type ResourceConfig struct {
// CPUs to be assigned to the VM
diff --git a/pkg/machine/e2e/inspect_test.go b/pkg/machine/e2e/inspect_test.go
index e282dd21d..b34285dd8 100644
--- a/pkg/machine/e2e/inspect_test.go
+++ b/pkg/machine/e2e/inspect_test.go
@@ -43,9 +43,11 @@ var _ = Describe("podman machine stop", func() {
Expect(foo2).To(Exit(0))
inspect := new(inspectMachine)
+ inspect = inspect.withFormat("{{.Name}}")
inspectSession, err := mb.setName("foo1").setCmd(inspect).run()
Expect(err).To(BeNil())
Expect(inspectSession).To(Exit(0))
+ Expect(inspectSession.Bytes()).To(ContainSubstring("foo1"))
type fakeInfos struct {
Status string
@@ -56,13 +58,13 @@ var _ = Describe("podman machine stop", func() {
Expect(err).ToNot(HaveOccurred())
Expect(len(infos)).To(Equal(2))
- //rm := new(rmMachine)
- //// Must manually clean up due to multiple names
- //for _, name := range []string{"foo1", "foo2"} {
+ // rm := new(rmMachine)
+ // // Must manually clean up due to multiple names
+ // for _, name := range []string{"foo1", "foo2"} {
// mb.setName(name).setCmd(rm.withForce()).run()
// mb.names = []string{}
- //}
- //mb.names = []string{}
+ // }
+ // mb.names = []string{}
})
})
diff --git a/pkg/machine/e2e/machine_test.go b/pkg/machine/e2e/machine_test.go
index 2b3b60b2b..657014b05 100644
--- a/pkg/machine/e2e/machine_test.go
+++ b/pkg/machine/e2e/machine_test.go
@@ -23,14 +23,20 @@ func TestMain(m *testing.M) {
const (
defaultStream string = "podman-testing"
- tmpDir string = "/var/tmp"
)
var (
+ tmpDir = "/var/tmp"
fqImageName string
suiteImageName string
)
+func init() {
+ if value, ok := os.LookupEnv("TMPDIR"); ok {
+ tmpDir = value
+ }
+}
+
// TestLibpod ginkgo master function
func TestMachine(t *testing.T) {
RegisterFailHandler(Fail)
@@ -70,7 +76,8 @@ var _ = SynchronizedAfterSuite(func() {},
})
func setup() (string, *machineTestBuilder) {
- homeDir, err := ioutil.TempDir("/var/tmp", "podman_test")
+ // Set TMPDIR if this needs a new directory
+ homeDir, err := ioutil.TempDir("", "podman_test")
if err != nil {
Fail(fmt.Sprintf("failed to create home directory: %q", err))
}
diff --git a/pkg/machine/qemu/config_test.go b/pkg/machine/qemu/config_test.go
index 0fbb5b3bf..3f92881fa 100644
--- a/pkg/machine/qemu/config_test.go
+++ b/pkg/machine/qemu/config_test.go
@@ -68,9 +68,15 @@ func TestNewMachineFile(t *testing.T) {
p := "/var/tmp/podman/my.sock"
longp := filepath.Join(longTemp, utils.RandomString(100), "my.sock")
- os.MkdirAll(filepath.Dir(longp), 0755)
- f, _ := os.Create(longp)
- f.Close()
+ err = os.MkdirAll(filepath.Dir(longp), 0755)
+ if err != nil {
+ panic(err)
+ }
+ f, err := os.Create(longp)
+ if err != nil {
+ panic(err)
+ }
+ _ = f.Close()
sym := "my.sock"
longSym := filepath.Join(homedir, ".podman", sym)
@@ -120,14 +126,15 @@ func TestNewMachineFile(t *testing.T) {
},
}
for _, tt := range tests {
+ tt := tt
t.Run(tt.name, func(t *testing.T) {
- got, err := machine.NewMachineFile(tt.args.path, tt.args.symlink) //nolint: scopelint
- if (err != nil) != tt.wantErr { //nolint: scopelint
- t.Errorf("NewMachineFile() error = %v, wantErr %v", err, tt.wantErr) //nolint: scopelint
+ got, err := machine.NewMachineFile(tt.args.path, tt.args.symlink)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("NewMachineFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got, tt.want) { //nolint: scopelint
- t.Errorf("NewMachineFile() got = %v, want %v", got, tt.want) //nolint: scopelint
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("NewMachineFile() got = %v, want %v", got, tt.want)
}
})
}
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index 35eea5fb4..6e36b0886 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -484,12 +484,11 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
if err := v.writeConfig(); err != nil {
return fmt.Errorf("writing JSON file: %w", err)
}
- defer func() error {
+ defer func() {
v.Starting = false
if err := v.writeConfig(); err != nil {
- return fmt.Errorf("writing JSON file: %w", err)
+ logrus.Errorf("Writing JSON file: %v", err)
}
- return nil
}()
if v.isIncompatible() {
logrus.Errorf("machine %q is incompatible with this release of podman and needs to be recreated, starting for recovery only", v.Name)
@@ -526,10 +525,11 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
time.Sleep(wait)
wait++
}
- defer qemuSocketConn.Close()
if err != nil {
return err
}
+ defer qemuSocketConn.Close()
+
fd, err := qemuSocketConn.(*net.UnixConn).File()
if err != nil {
return err
@@ -1544,3 +1544,79 @@ func (v *MachineVM) editCmdLine(flag string, value string) {
v.CmdLine = append(v.CmdLine, []string{flag, value}...)
}
}
+
+// RemoveAndCleanMachines removes all machine and cleans up any other files associatied with podman machine
+func (p *Provider) RemoveAndCleanMachines() error {
+ var (
+ vm machine.VM
+ listResponse []*machine.ListResponse
+ opts machine.ListOptions
+ destroyOptions machine.RemoveOptions
+ )
+ destroyOptions.Force = true
+ var prevErr error
+
+ listResponse, err := p.List(opts)
+ if err != nil {
+ return err
+ }
+
+ for _, mach := range listResponse {
+ vm, err = p.LoadVMByName(mach.Name)
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ }
+ _, remove, err := vm.Remove(mach.Name, destroyOptions)
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ } else {
+ if err := remove(); err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ }
+ }
+ }
+
+ // Clean leftover files in data dir
+ dataDir, err := machine.DataDirPrefix()
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ } else {
+ err := os.RemoveAll(dataDir)
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ }
+ }
+
+ // Clean leftover files in conf dir
+ confDir, err := machine.ConfDirPrefix()
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ } else {
+ err := os.RemoveAll(confDir)
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ }
+ }
+ return prevErr
+}
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index addcb7c8a..57fb36fc9 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -18,7 +18,6 @@ import (
"strings"
"time"
- "github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/utils"
"github.com/containers/storage/pkg/homedir"
@@ -153,20 +152,22 @@ const (
type Provider struct{}
type MachineVM struct {
- // IdentityPath is the fq path to the ssh priv key
- IdentityPath string
+ // ConfigPath is the path to the configuration file
+ ConfigPath string
+ // Created contains the original created time instead of querying the file mod time
+ Created time.Time
// ImageStream is the version of fcos being used
ImageStream string
// ImagePath is the fq path to
ImagePath string
+ // LastUp contains the last recorded uptime
+ LastUp time.Time
// Name of the vm
Name string
- // SSH port for user networking
- Port int
- // RemoteUsername of the vm user
- RemoteUsername string
// Whether this machine should run in a rootful or rootless manner
Rootful bool
+ // SSH identity, username, etc
+ machine.SSHConfig
}
type ExitCodeError struct {
@@ -181,16 +182,22 @@ func GetWSLProvider() machine.Provider {
return wslProvider
}
-// NewMachine initializes an instance of a virtual machine based on the qemu
-// virtualization.
+// NewMachine initializes an instance of a wsl machine
func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
vm := new(MachineVM)
if len(opts.Name) > 0 {
vm.Name = opts.Name
}
+ configPath, err := getConfigPath(opts.Name)
+ if err != nil {
+ return vm, err
+ }
+ vm.ConfigPath = configPath
vm.ImagePath = opts.ImagePath
vm.RemoteUsername = opts.Username
+ vm.Created = time.Now()
+ vm.LastUp = vm.Created
// Add a random port for ssh
port, err := utils.GetRandomPort()
@@ -202,25 +209,69 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
return vm, nil
}
+func getConfigPath(name string) (string, error) {
+ vmConfigDir, err := machine.GetConfDir(vmtype)
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(vmConfigDir, name+".json"), nil
+}
+
// LoadByName reads a json file that describes a known qemu vm
// and returns a vm instance
func (p *Provider) LoadVMByName(name string) (machine.VM, error) {
- vm := new(MachineVM)
- vmConfigDir, err := machine.GetConfDir(vmtype)
+ configPath, err := getConfigPath(name)
if err != nil {
return nil, err
}
- b, err := ioutil.ReadFile(filepath.Join(vmConfigDir, name+".json"))
- if os.IsNotExist(err) {
- return nil, errors.Wrap(machine.ErrNoSuchVM, name)
- }
+
+ vm, err := readAndMigrate(configPath, name)
+ return vm, err
+}
+
+// readAndMigrate returns the content of the VM's
+// configuration file in json
+func readAndMigrate(configPath string, name string) (*MachineVM, error) {
+ vm := new(MachineVM)
+ b, err := os.ReadFile(configPath)
if err != nil {
- return nil, err
+ if errors.Is(err, os.ErrNotExist) {
+ return nil, errors.Wrap(machine.ErrNoSuchVM, name)
+ }
+ return vm, err
}
err = json.Unmarshal(b, vm)
+ if err == nil && vm.Created.IsZero() {
+ err = vm.migrate40(configPath)
+ }
return vm, err
}
+func (v *MachineVM) migrate40(configPath string) error {
+ v.ConfigPath = configPath
+ fi, err := os.Stat(configPath)
+ if err != nil {
+ return err
+ }
+ v.Created = fi.ModTime()
+ v.LastUp = getLegacyLastStart(v)
+ return v.writeConfig()
+}
+
+func getLegacyLastStart(vm *MachineVM) time.Time {
+ vmDataDir, err := machine.GetDataDir(vmtype)
+ if err != nil {
+ return vm.Created
+ }
+ distDir := filepath.Join(vmDataDir, "wsldist")
+ start := filepath.Join(distDir, vm.Name, "laststart")
+ info, err := os.Stat(start)
+ if err != nil {
+ return vm.Created
+ }
+ return info.ModTime()
+}
+
// Init writes the json configuration file to the filesystem for
// other verbs (start, stop)
func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
@@ -289,12 +340,7 @@ func downloadDistro(v *MachineVM, opts machine.InitOptions) error {
}
func (v *MachineVM) writeConfig() error {
- vmConfigDir, err := machine.GetConfDir(vmtype)
- if err != nil {
- return err
- }
-
- jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json"
+ jsonFile := v.ConfigPath
b, err := json.MarshalIndent(v, "", " ")
if err != nil {
@@ -810,7 +856,8 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
}
}
- return markStart(name)
+ _, _, err = v.updateTimeStamps(true)
+ return err
}
func launchWinProxy(v *MachineVM) (bool, string, error) {
@@ -1005,6 +1052,8 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return errors.Errorf("%q is not running", v.Name)
}
+ _, _, _ = v.updateTimeStamps(true)
+
if err := stopWinProxy(v); err != nil {
fmt.Fprintf(os.Stderr, "Could not stop API forwarding service (win-sshproxy.exe): %s\n", err.Error())
}
@@ -1032,10 +1081,12 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return nil
}
-// TODO: We need to rename isRunning to State(); I do not have a
-// windows system to test this on.
func (v *MachineVM) State(bypass bool) (machine.Status, error) {
- return "", define.ErrNotImplemented
+ if v.isRunning() {
+ return machine.Running, nil
+ }
+
+ return machine.Stopped, nil
}
func stopWinProxy(v *MachineVM) error {
@@ -1210,14 +1261,9 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
var listed []*machine.ListResponse
if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error {
- vm := new(MachineVM)
if strings.HasSuffix(d.Name(), ".json") {
- fullPath := filepath.Join(vmConfigDir, d.Name())
- b, err := ioutil.ReadFile(fullPath)
- if err != nil {
- return err
- }
- err = json.Unmarshal(b, vm)
+ path := filepath.Join(vmConfigDir, d.Name())
+ vm, err := readAndMigrate(path, strings.TrimSuffix(d.Name(), ".json"))
if err != nil {
return err
}
@@ -1229,15 +1275,13 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
listEntry.CPUs, _ = getCPUs(vm)
listEntry.Memory, _ = getMem(vm)
listEntry.DiskSize = getDiskSize(vm)
- fi, err := os.Stat(fullPath)
- if err != nil {
- return err
- }
- listEntry.CreatedAt = fi.ModTime()
- listEntry.LastUp = getLastStart(vm, fi.ModTime())
- if vm.isRunning() {
- listEntry.Running = true
- }
+ listEntry.RemoteUsername = vm.RemoteUsername
+ listEntry.Port = vm.Port
+ listEntry.IdentityPath = vm.IdentityPath
+
+ running := vm.isRunning()
+ listEntry.CreatedAt, listEntry.LastUp, _ = vm.updateTimeStamps(running)
+ listEntry.Running = running
listed = append(listed, listEntry)
}
@@ -1248,6 +1292,16 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
return listed, err
}
+func (vm *MachineVM) updateTimeStamps(updateLast bool) (time.Time, time.Time, error) {
+ var err error
+ if updateLast {
+ vm.LastUp = time.Now()
+ err = vm.writeConfig()
+ }
+
+ return vm.Created, vm.LastUp, err
+}
+
func getDiskSize(vm *MachineVM) uint64 {
vmDataDir, err := machine.GetDataDir(vmtype)
if err != nil {
@@ -1262,36 +1316,6 @@ func getDiskSize(vm *MachineVM) uint64 {
return uint64(info.Size())
}
-func markStart(name string) error {
- vmDataDir, err := machine.GetDataDir(vmtype)
- if err != nil {
- return err
- }
- distDir := filepath.Join(vmDataDir, "wsldist")
- start := filepath.Join(distDir, name, "laststart")
- file, err := os.Create(start)
- if err != nil {
- return err
- }
- file.Close()
-
- return nil
-}
-
-func getLastStart(vm *MachineVM, created time.Time) time.Time {
- vmDataDir, err := machine.GetDataDir(vmtype)
- if err != nil {
- return created
- }
- distDir := filepath.Join(vmDataDir, "wsldist")
- start := filepath.Join(distDir, vm.Name, "laststart")
- info, err := os.Stat(start)
- if err != nil {
- return created
- }
- return info.ModTime()
-}
-
func getCPUs(vm *MachineVM) (uint64, error) {
dist := toDist(vm.Name)
if run, _ := isWSLRunning(dist); !run {
@@ -1388,6 +1412,109 @@ func (v *MachineVM) setRootful(rootful bool) error {
return nil
}
+// Inspect returns verbose detail about the machine
func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
- return nil, define.ErrNotImplemented
+ state, err := v.State(false)
+ if err != nil {
+ return nil, err
+ }
+
+ created, lastUp, _ := v.updateTimeStamps(state == machine.Running)
+
+ return &machine.InspectInfo{
+ ConfigPath: machine.VMFile{Path: v.ConfigPath},
+ Created: created,
+ Image: machine.ImageConfig{
+ ImagePath: machine.VMFile{Path: v.ImagePath},
+ ImageStream: v.ImageStream,
+ },
+ LastUp: lastUp,
+ Name: v.Name,
+ Resources: v.getResources(),
+ SSHConfig: v.SSHConfig,
+ State: state,
+ }, nil
+}
+
+func (v *MachineVM) getResources() (resources machine.ResourceConfig) {
+ resources.CPUs, _ = getCPUs(v)
+ resources.Memory, _ = getMem(v)
+ resources.DiskSize = getDiskSize(v)
+ return
+}
+
+// RemoveAndCleanMachines removes all machine and cleans up any other files associatied with podman machine
+func (p *Provider) RemoveAndCleanMachines() error {
+ var (
+ vm machine.VM
+ listResponse []*machine.ListResponse
+ opts machine.ListOptions
+ destroyOptions machine.RemoveOptions
+ )
+ destroyOptions.Force = true
+ var prevErr error
+
+ listResponse, err := p.List(opts)
+ if err != nil {
+ return err
+ }
+
+ for _, mach := range listResponse {
+ vm, err = p.LoadVMByName(mach.Name)
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ }
+ _, remove, err := vm.Remove(mach.Name, destroyOptions)
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ } else {
+ if err := remove(); err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ }
+ }
+ }
+
+ // Clean leftover files in data dir
+ dataDir, err := machine.DataDirPrefix()
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ } else {
+ err := os.RemoveAll(dataDir)
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ }
+ }
+
+ // Clean leftover files in conf dir
+ confDir, err := machine.ConfDirPrefix()
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ } else {
+ err := os.RemoveAll(confDir)
+ if err != nil {
+ if prevErr != nil {
+ logrus.Error(prevErr)
+ }
+ prevErr = err
+ }
+ }
+ return prevErr
}
diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
index a3408b402..fce32d688 100644
--- a/pkg/specgen/generate/pod_create.go
+++ b/pkg/specgen/generate/pod_create.go
@@ -197,6 +197,8 @@ func createPodOptions(p *specgen.PodSpecGenerator) ([]libpod.PodCreateOption, er
options = append(options, libpod.WithPodHostname(p.Hostname))
}
+ options = append(options, libpod.WithPodExitPolicy(p.ExitPolicy))
+
return options, nil
}
diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go
index 759caa0c0..1bb64448f 100644
--- a/pkg/specgen/podspecgen.go
+++ b/pkg/specgen/podspecgen.go
@@ -19,6 +19,8 @@ type PodBasicConfig struct {
// all containers in the pod as long as the UTS namespace is shared.
// Optional.
Hostname string `json:"hostname,omitempty"`
+ // ExitPolicy determines the pod's exit and stop behaviour.
+ ExitPolicy string `json:"exit_policy,omitempty"`
// Labels are key-value pairs that are used to add metadata to pods.
// Optional.
Labels map[string]string `json:"labels,omitempty"`
diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go
index cd1486a82..4043fbfcb 100644
--- a/pkg/systemd/generate/pods.go
+++ b/pkg/systemd/generate/pods.go
@@ -256,6 +256,16 @@ func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) (
return &info, nil
}
+// Unless already specified, the pod's exit policy to "stop".
+func setPodExitPolicy(cmd []string) []string {
+ for _, arg := range cmd {
+ if strings.HasPrefix(arg, "--exit-policy=") || arg == "--exit-policy" {
+ return cmd
+ }
+ }
+ return append(cmd, "--exit-policy=stop")
+}
+
// executePodTemplate executes the pod template on the specified podInfo. Note
// that the podInfo is also post processed and completed, which allows for an
// easier unit testing.
@@ -355,6 +365,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
}
startCommand = append(startCommand, podCreateArgs...)
+ startCommand = setPodExitPolicy(startCommand)
startCommand = escapeSystemdArguments(startCommand)
info.ExecStartPre1 = "/bin/rm -f {{{{.PIDFile}}}} {{{{.PodIDFile}}}}"
diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go
index dcb18780c..59f217256 100644
--- a/pkg/systemd/generate/pods_test.go
+++ b/pkg/systemd/generate/pods_test.go
@@ -7,6 +7,28 @@ import (
"github.com/stretchr/testify/assert"
)
+func TestSetPodExitPolicy(t *testing.T) {
+ tests := []struct {
+ input, expected []string
+ }{
+ {
+ []string{"podman", "pod", "create"},
+ []string{"podman", "pod", "create", "--exit-policy=stop"},
+ },
+ {
+ []string{"podman", "pod", "create", "--exit-policy=continue"},
+ []string{"podman", "pod", "create", "--exit-policy=continue"},
+ },
+ {
+ []string{"podman", "pod", "create", "--exit-policy", "continue"},
+ []string{"podman", "pod", "create", "--exit-policy", "continue"},
+ },
+ }
+ for _, test := range tests {
+ assert.Equalf(t, test.expected, setPodExitPolicy(test.input), "%v", test.input)
+ }
+}
+
func TestValidateRestartPolicyPod(t *testing.T) {
type podInfo struct {
restart string
@@ -252,7 +274,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id
-ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace
+ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace --exit-policy=stop
ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id
ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10
ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id
@@ -280,7 +302,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id
-ExecStartPre=/usr/bin/podman --events-backend none --runroot /root pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace
+ExecStartPre=/usr/bin/podman --events-backend none --runroot /root pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace --exit-policy=stop
ExecStart=/usr/bin/podman --events-backend none --runroot /root pod start --pod-id-file %t/pod-123abc.pod-id
ExecStop=/usr/bin/podman --events-backend none --runroot /root pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10
ExecStopPost=/usr/bin/podman --events-backend none --runroot /root pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id
@@ -308,7 +330,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id
-ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo --replace
+ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo --replace --exit-policy=stop
ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id
ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10
ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id
@@ -336,7 +358,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id
-ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo --label key={{someval}} --replace
+ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo --label key={{someval}} --exit-policy=continue --replace
ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id
ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10
ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id
@@ -581,7 +603,7 @@ WantedBy=default.target
GraphRoot: "/var/lib/containers/storage",
RunRoot: "/var/run/containers/storage",
RequiredServices: []string{"container-1", "container-2"},
- CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "--label", "key={{someval}}"},
+ CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "--label", "key={{someval}}", "--exit-policy=continue"},
},
podNewLabelWithCurlyBraces,
true,
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 9842a0f73..a0bf8b50d 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -731,29 +731,6 @@ func IDtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxI
return convertedIDMap
}
-var socketPath string
-
-func SetSocketPath(path string) {
- socketPath = path
-}
-
-func SocketPath() (string, error) {
- if socketPath != "" {
- return socketPath, nil
- }
- xdg, err := GetRuntimeDir()
- if err != nil {
- return "", err
- }
- if len(xdg) == 0 {
- // If no xdg is returned, assume root socket
- xdg = "/run"
- }
-
- // Glue the socket path together
- return filepath.Join(xdg, "podman", "podman.sock"), nil
-}
-
func LookupUser(name string) (*user.User, error) {
// Assume UID look up first, if it fails lookup by username
if u, err := user.LookupId(name); err == nil {