summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/images_build.go26
-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/bindings/generator/generator.go2
-rw-r--r--pkg/bindings/images/build.go10
-rw-r--r--pkg/domain/entities/pods.go2
-rw-r--r--pkg/domain/infra/abi/containers.go12
-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/hooks/hooks_test.go17
-rw-r--r--pkg/hooks/monitor_test.go19
-rw-r--r--pkg/hooks/read_test.go40
-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.go35
-rw-r--r--pkg/machine/qemu/machine.go84
-rw-r--r--pkg/machine/wsl/machine.go273
-rw-r--r--pkg/specgen/generate/container.go8
-rw-r--r--pkg/specgen/generate/container_create.go16
-rw-r--r--pkg/specgen/generate/kube/play_test.go10
-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
30 files changed, 748 insertions, 265 deletions
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 0f85aa717..f0d07f492 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -72,25 +72,26 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
query := struct {
AddHosts string `schema:"extrahosts"`
AdditionalCapabilities string `schema:"addcaps"`
+ AllPlatforms bool `schema:"allplatforms"`
Annotations string `schema:"annotations"`
AppArmor string `schema:"apparmor"`
- AllPlatforms bool `schema:"allplatforms"`
BuildArgs string `schema:"buildargs"`
CacheFrom string `schema:"cachefrom"`
+ CgroupParent string `schema:"cgroupparent"` // nolint
Compression uint64 `schema:"compression"`
ConfigureNetwork string `schema:"networkmode"`
- CpuPeriod uint64 `schema:"cpuperiod"` // nolint
- CpuQuota int64 `schema:"cpuquota"` // nolint
- CpuSetCpus string `schema:"cpusetcpus"` // nolint
- CpuSetMems string `schema:"cpusetmems"` // nolint
- CpuShares uint64 `schema:"cpushares"` // nolint
- CgroupParent string `schema:"cgroupparent"` // nolint
+ CpuPeriod uint64 `schema:"cpuperiod"` // nolint
+ CpuQuota int64 `schema:"cpuquota"` // nolint
+ CpuSetCpus string `schema:"cpusetcpus"` // nolint
+ CpuSetMems string `schema:"cpusetmems"` // nolint
+ CpuShares uint64 `schema:"cpushares"` // nolint
DNSOptions string `schema:"dnsoptions"`
DNSSearch string `schema:"dnssearch"`
DNSServers string `schema:"dnsservers"`
Devices string `schema:"devices"`
Dockerfile string `schema:"dockerfile"`
DropCapabilities string `schema:"dropcaps"`
+ Envs []string `schema:"setenv"`
Excludes string `schema:"excludes"`
ForceRm bool `schema:"forcerm"`
From string `schema:"from"`
@@ -108,6 +109,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Memory int64 `schema:"memory"`
NamespaceOptions string `schema:"nsoptions"`
NoCache bool `schema:"nocache"`
+ OSFeatures []string `schema:"osfeature"`
+ OSVersion string `schema:"osversion"`
OutputFormat string `schema:"outputformat"`
Platform []string `schema:"platform"`
Pull bool `schema:"pull"`
@@ -117,16 +120,16 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Rm bool `schema:"rm"`
RusageLogFile string `schema:"rusagelogfile"`
Seccomp string `schema:"seccomp"`
+ Secrets string `schema:"secrets"`
SecurityOpt string `schema:"securityopt"`
ShmSize int `schema:"shmsize"`
Squash bool `schema:"squash"`
+ TLSVerify bool `schema:"tlsVerify"`
Tags []string `schema:"t"`
Target string `schema:"target"`
Timestamp int64 `schema:"timestamp"`
- TLSVerify bool `schema:"tlsVerify"`
Ulimits string `schema:"ulimits"`
UnsetEnvs []string `schema:"unsetenv"`
- Secrets string `schema:"secrets"`
}{
Dockerfile: "Dockerfile",
IdentityLabel: true,
@@ -544,6 +547,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
ContextDirectory: contextDirectory,
Devices: devices,
DropCapabilities: dropCaps,
+ Envs: query.Envs,
Err: auxout,
Excludes: excludes,
ForceRmIntermediateCtrs: query.ForceRm,
@@ -558,6 +562,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
MaxPullPushRetries: 3,
NamespaceOptions: nsoptions,
NoCache: query.NoCache,
+ OSFeatures: query.OSFeatures,
+ OSVersion: query.OSVersion,
Out: stdout,
Output: output,
OutputFormat: format,
@@ -569,8 +575,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
ReportWriter: reporter,
RusageLogFile: query.RusageLogFile,
Squash: query.Squash,
- Target: query.Target,
SystemContext: systemContext,
+ Target: query.Target,
UnsetEnvs: query.UnsetEnvs,
}
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/bindings/generator/generator.go b/pkg/bindings/generator/generator.go
index e69973be1..06be52451 100644
--- a/pkg/bindings/generator/generator.go
+++ b/pkg/bindings/generator/generator.go
@@ -171,7 +171,7 @@ func main() {
}
// go import file
- goimport := exec.Command("goimports", "-w", out.Name())
+ goimport := exec.Command("../../../test/tools/build/goimports", "-w", out.Name())
goimport.Stderr = os.Stdout
if err := goimport.Run(); err != nil {
fmt.Println(err)
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index 9e0a0d798..51dcd2aa5 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -216,6 +216,12 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if t := options.Output; len(t) > 0 {
params.Set("output", t)
}
+ if t := options.OSVersion; len(t) > 0 {
+ params.Set("osversion", t)
+ }
+ for _, t := range options.OSFeatures {
+ params.Set("osfeature", t)
+ }
var platform string
if len(options.OS) > 0 {
platform = options.OS
@@ -303,6 +309,10 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
params.Set("ulimits", string(ulimitsJSON))
}
+ for _, env := range options.Envs {
+ params.Add("setenv", env)
+ }
+
for _, uenv := range options.UnsetEnvs {
params.Add("unsetenv", uenv)
}
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 b56c36015..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
}
@@ -1544,6 +1548,12 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti
return nil, err
}
+ if len(spec.Networks) > 0 && pod.SharesNet() {
+ logrus.Warning("resetting network config, cannot specify a network other than the pod's when sharing the net namespace")
+ spec.Networks = nil
+ spec.NetworkOptions = nil
+ }
+
allNamespaces := []struct {
isShared bool
value *specgen.Namespace
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/hooks/hooks_test.go b/pkg/hooks/hooks_test.go
index fb3c63250..d5d0c2a32 100644
--- a/pkg/hooks/hooks_test.go
+++ b/pkg/hooks/hooks_test.go
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io/ioutil"
- "os"
"path/filepath"
"runtime"
"testing"
@@ -20,11 +19,7 @@ var path string
func TestGoodNew(t *testing.T) {
ctx := context.Background()
- dir, err := ioutil.TempDir("", "hooks-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+ dir := t.TempDir()
for i, name := range []string{
"01-my-hook.json",
@@ -36,7 +31,7 @@ func TestGoodNew(t *testing.T) {
if i == 0 {
extraStages = ", \"poststart\", \"poststop\""
}
- err = ioutil.WriteFile(jsonPath, []byte(fmt.Sprintf("{\"version\": \"1.0.0\", \"hook\": {\"path\": \"%s\", \"timeout\": %d}, \"when\": {\"always\": true}, \"stages\": [\"prestart\"%s]}", path, i+1, extraStages)), 0644)
+ err := ioutil.WriteFile(jsonPath, []byte(fmt.Sprintf("{\"version\": \"1.0.0\", \"hook\": {\"path\": \"%s\", \"timeout\": %d}, \"when\": {\"always\": true}, \"stages\": [\"prestart\"%s]}", path, i+1, extraStages)), 0644)
if err != nil {
t.Fatal(err)
}
@@ -92,14 +87,10 @@ func TestGoodNew(t *testing.T) {
func TestBadNew(t *testing.T) {
ctx := context.Background()
- dir, err := ioutil.TempDir("", "hooks-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+ dir := t.TempDir()
jsonPath := filepath.Join(dir, "a.json")
- err = ioutil.WriteFile(jsonPath, []byte("{\"version\": \"-1\"}"), 0644)
+ err := ioutil.WriteFile(jsonPath, []byte("{\"version\": \"-1\"}"), 0644)
if err != nil {
t.Fatal(err)
}
diff --git a/pkg/hooks/monitor_test.go b/pkg/hooks/monitor_test.go
index eed02e033..1067d2920 100644
--- a/pkg/hooks/monitor_test.go
+++ b/pkg/hooks/monitor_test.go
@@ -15,11 +15,7 @@ import (
func TestMonitorOneDirGood(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
- dir, err := ioutil.TempDir("", "hooks-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+ dir := t.TempDir()
manager, err := New(ctx, []string{dir}, []string{})
if err != nil {
@@ -114,17 +110,8 @@ func TestMonitorOneDirGood(t *testing.T) {
func TestMonitorTwoDirGood(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
- primaryDir, err := ioutil.TempDir("", "hooks-test-primary-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(primaryDir)
-
- fallbackDir, err := ioutil.TempDir("", "hooks-test-fallback-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(fallbackDir)
+ primaryDir := t.TempDir()
+ fallbackDir := t.TempDir()
manager, err := New(ctx, []string{fallbackDir, primaryDir}, []string{})
if err != nil {
diff --git a/pkg/hooks/read_test.go b/pkg/hooks/read_test.go
index 6e6c190bb..381d66bbe 100644
--- a/pkg/hooks/read_test.go
+++ b/pkg/hooks/read_test.go
@@ -29,14 +29,10 @@ func TestUnknownPath(t *testing.T) {
}
func TestGoodFile(t *testing.T) {
- dir, err := ioutil.TempDir("", "hooks-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+ dir := t.TempDir()
jsonPath := filepath.Join(dir, "hook.json")
- err = ioutil.WriteFile(jsonPath, []byte(fmt.Sprintf("{\"version\": \"1.0.0\", \"hook\": {\"path\": \"%s\"}, \"when\": {\"always\": true}, \"stages\": [\"prestart\"]}", path)), 0644)
+ err := ioutil.WriteFile(jsonPath, []byte(fmt.Sprintf("{\"version\": \"1.0.0\", \"hook\": {\"path\": \"%s\"}, \"when\": {\"always\": true}, \"stages\": [\"prestart\"]}", path)), 0644)
if err != nil {
t.Fatal(err)
}
@@ -59,14 +55,10 @@ func TestGoodFile(t *testing.T) {
}
func TestBadFile(t *testing.T) {
- dir, err := ioutil.TempDir("", "hooks-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+ dir := t.TempDir()
path := filepath.Join(dir, "hook.json")
- err = ioutil.WriteFile(path, []byte("{\"version\": \"1.0.0\", \"hook\": \"not-a-string\"}"), 0644)
+ err := ioutil.WriteFile(path, []byte("{\"version\": \"1.0.0\", \"hook\": \"not-a-string\"}"), 0644)
if err != nil {
t.Fatal(err)
}
@@ -121,13 +113,9 @@ func TestInvalidCurrentJSON(t *testing.T) {
}
func TestGoodDir(t *testing.T) {
- dir, err := ioutil.TempDir("", "hooks-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+ dir := t.TempDir()
- err = ioutil.WriteFile(filepath.Join(dir, "README"), []byte("not a hook"), 0644)
+ err := ioutil.WriteFile(filepath.Join(dir, "README"), []byte("not a hook"), 0644)
if err != nil {
t.Fatal(err)
}
@@ -172,14 +160,10 @@ func TestUnknownDir(t *testing.T) {
}
func TestBadDir(t *testing.T) {
- dir, err := ioutil.TempDir("", "hooks-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+ dir := t.TempDir()
jsonPath := filepath.Join(dir, "a.json")
- err = ioutil.WriteFile(jsonPath, []byte("{\"version\": \"-1\"}"), 0644)
+ err := ioutil.WriteFile(jsonPath, []byte("{\"version\": \"-1\"}"), 0644)
if err != nil {
t.Fatal(err)
}
@@ -193,14 +177,10 @@ func TestBadDir(t *testing.T) {
}
func TestHookExecutableDoesNotExit(t *testing.T) {
- dir, err := ioutil.TempDir("", "hooks-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+ dir := t.TempDir()
jsonPath := filepath.Join(dir, "hook.json")
- err = ioutil.WriteFile(jsonPath, []byte("{\"version\": \"1.0.0\", \"hook\": {\"path\": \"/does/not/exist\"}, \"when\": {\"always\": true}, \"stages\": [\"prestart\"]}"), 0644)
+ err := ioutil.WriteFile(jsonPath, []byte("{\"version\": \"1.0.0\", \"hook\": {\"path\": \"/does/not/exist\"}, \"when\": {\"always\": true}, \"stages\": [\"prestart\"]}"), 0644)
if err != nil {
t.Fatal(err)
}
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..4d96ec6e7 100644
--- a/pkg/machine/qemu/config_test.go
+++ b/pkg/machine/qemu/config_test.go
@@ -52,25 +52,23 @@ func TestMachineFile_GetPath(t *testing.T) {
func TestNewMachineFile(t *testing.T) {
empty := ""
- homedir, err := os.MkdirTemp("/tmp", "homedir")
- if err != nil {
- panic(err)
- }
- defer os.RemoveAll(homedir)
- longTemp, err := os.MkdirTemp("/tmp", "tmpdir")
- if err != nil {
- panic(err)
- }
- defer os.RemoveAll(longTemp)
+ homedir := t.TempDir()
+ longTemp := t.TempDir()
oldhome := os.Getenv("HOME")
os.Setenv("HOME", homedir) //nolint: tenv
defer os.Setenv("HOME", oldhome)
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 +118,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/container.go b/pkg/specgen/generate/container.go
index 831c1d7b9..d8008b10b 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -3,6 +3,7 @@ package generate
import (
"context"
"encoding/json"
+ "fmt"
"os"
"strings"
"time"
@@ -352,7 +353,10 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s
if err != nil {
return nil, nil, err
}
- conf := c.Config()
+ conf := c.ConfigWithNetworks()
+ if conf == nil {
+ return nil, nil, fmt.Errorf("failed to get config for container %s", c.ID())
+ }
tmpSystemd := conf.Systemd
tmpMounts := conf.Mounts
@@ -501,6 +505,8 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s
_, mounts := c.SortUserVolumes(c.Spec())
specg.Mounts = mounts
specg.HostDeviceList = conf.DeviceHostSrc
+ specg.Networks = conf.Networks
+
mapSecurityConfig(conf, specg)
if c.IsInfra() { // if we are creating this spec for a pod's infra ctr, map the compatible options
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 88a452cbb..04e24d625 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -175,13 +175,15 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, nil, nil, errors.New("the given container could not be retrieved")
}
conf := c.Config()
- out, err := json.Marshal(conf.Spec.Linux)
- if err != nil {
- return nil, nil, nil, err
- }
- err = json.Unmarshal(out, runtimeSpec.Linux)
- if err != nil {
- return nil, nil, nil, err
+ if conf != nil && conf.Spec != nil && conf.Spec.Linux != nil {
+ out, err := json.Marshal(conf.Spec.Linux)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ err = json.Unmarshal(out, runtimeSpec.Linux)
+ if err != nil {
+ return nil, nil, nil, err
+ }
}
if s.ResourceLimits != nil {
switch {
diff --git a/pkg/specgen/generate/kube/play_test.go b/pkg/specgen/generate/kube/play_test.go
index 448522c2a..e01d62b08 100644
--- a/pkg/specgen/generate/kube/play_test.go
+++ b/pkg/specgen/generate/kube/play_test.go
@@ -3,9 +3,7 @@ package kube
import (
"encoding/json"
"fmt"
- "io/ioutil"
"math"
- "os"
"runtime"
"strconv"
"testing"
@@ -39,9 +37,7 @@ func createSecrets(t *testing.T, d string) *secrets.SecretsManager {
}
func TestEnvVarsFrom(t *testing.T) {
- d, err := ioutil.TempDir("", "secrets")
- assert.NoError(t, err)
- defer os.RemoveAll(d)
+ d := t.TempDir()
secretsManager := createSecrets(t, d)
tests := []struct {
@@ -191,9 +187,7 @@ func TestEnvVarsFrom(t *testing.T) {
}
func TestEnvVarValue(t *testing.T) {
- d, err := ioutil.TempDir("", "secrets")
- assert.NoError(t, err)
- defer os.RemoveAll(d)
+ d := t.TempDir()
secretsManager := createSecrets(t, d)
stringNumCPUs := strconv.Itoa(runtime.NumCPU())
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 {