summaryrefslogtreecommitdiff
path: root/pkg/machine
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/machine')
-rw-r--r--pkg/machine/config.go30
-rw-r--r--pkg/machine/ignition.go127
-rw-r--r--pkg/machine/qemu/machine.go125
3 files changed, 236 insertions, 46 deletions
diff --git a/pkg/machine/config.go b/pkg/machine/config.go
index 33a352898..97237f5e5 100644
--- a/pkg/machine/config.go
+++ b/pkg/machine/config.go
@@ -29,6 +29,15 @@ type InitOptions struct {
ReExec bool
}
+type QemuMachineStatus = string
+
+const (
+ // Running indicates the qemu vm is running
+ Running QemuMachineStatus = "running"
+ // Stopped indicates the vm has stopped
+ Stopped QemuMachineStatus = "stopped"
+)
+
type Provider interface {
NewMachine(opts InitOptions) (VM, error)
LoadVMByName(name string) (VM, error)
@@ -66,15 +75,18 @@ type Download struct {
type ListOptions struct{}
type ListResponse struct {
- Name string
- CreatedAt time.Time
- LastUp time.Time
- Running bool
- Stream string
- VMType string
- CPUs uint64
- Memory uint64
- DiskSize uint64
+ Name string
+ CreatedAt time.Time
+ LastUp time.Time
+ Running bool
+ Stream string
+ VMType string
+ CPUs uint64
+ Memory uint64
+ DiskSize uint64
+ Port int
+ RemoteUsername string
+ IdentityPath string
}
type SSHOptions struct {
diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go
index 84d3be296..ac2cf71cf 100644
--- a/pkg/machine/ignition.go
+++ b/pkg/machine/ignition.go
@@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
+ "github.com/containers/common/pkg/config"
"github.com/sirupsen/logrus"
)
@@ -340,6 +341,24 @@ machine_enabled=true
},
})
+ setProxyOpts := getProxyVariables()
+ if setProxyOpts != "" {
+ files = append(files, File{
+ Node: Node{
+ Group: getNodeGrp("root"),
+ Path: "/etc/profile.d/proxy-opts.sh",
+ User: getNodeUsr("root"),
+ },
+ FileEmbedded1: FileEmbedded1{
+ Append: nil,
+ Contents: Resource{
+ Source: encodeDataURLPtr(setProxyOpts),
+ },
+ Mode: intToPtr(0644),
+ },
+ })
+ }
+
setDockerHost := `export DOCKER_HOST="unix://$(podman info -f "{{.Host.RemoteSocket.Path}}")"
`
@@ -365,52 +384,110 @@ machine_enabled=true
return files
}
- certFiles := getCerts(filepath.Join(userHome, ".config/containers/certs.d"))
+ certFiles := getCerts(filepath.Join(userHome, ".config/containers/certs.d"), true)
files = append(files, certFiles...)
- certFiles = getCerts(filepath.Join(userHome, ".config/docker/certs.d"))
+ certFiles = getCerts(filepath.Join(userHome, ".config/docker/certs.d"), true)
files = append(files, certFiles...)
+ if sslCertFile, ok := os.LookupEnv("SSL_CERT_FILE"); ok {
+ if _, err := os.Stat(sslCertFile); err == nil {
+ certFiles = getCerts(sslCertFile, false)
+ files = append(files, certFiles...)
+
+ if len(certFiles) > 0 {
+ setSSLCertFile := fmt.Sprintf("export %s=%s", "SSL_CERT_FILE", filepath.Join("/etc/containers/certs.d", filepath.Base(sslCertFile)))
+ files = append(files, File{
+ Node: Node{
+ Group: getNodeGrp("root"),
+ Path: "/etc/profile.d/ssl_cert_file.sh",
+ User: getNodeUsr("root"),
+ },
+ FileEmbedded1: FileEmbedded1{
+ Append: nil,
+ Contents: Resource{
+ Source: encodeDataURLPtr(setSSLCertFile),
+ },
+ Mode: intToPtr(0644),
+ },
+ })
+ }
+ }
+ }
+
return files
}
-func getCerts(certsDir string) []File {
+func getCerts(certsDir string, isDir bool) []File {
var (
files []File
)
certs, err := ioutil.ReadDir(certsDir)
- if err == nil {
- for _, cert := range certs {
- b, err := ioutil.ReadFile(filepath.Join(certsDir, cert.Name()))
- if err != nil {
- logrus.Warnf("Unable to read cert file %s", err.Error())
- continue
- }
- files = append(files, File{
- Node: Node{
- Group: getNodeGrp("root"),
- Path: filepath.Join("/etc/containers/certs.d/", cert.Name()),
- User: getNodeUsr("root"),
- },
- FileEmbedded1: FileEmbedded1{
- Append: nil,
- Contents: Resource{
- Source: encodeDataURLPtr(string(b)),
+ if isDir {
+ if err == nil {
+ for _, cert := range certs {
+ b, err := ioutil.ReadFile(filepath.Join(certsDir, cert.Name()))
+ if err != nil {
+ logrus.Warnf("Unable to read cert file %s", err.Error())
+ continue
+ }
+ files = append(files, File{
+ Node: Node{
+ Group: getNodeGrp("root"),
+ Path: filepath.Join("/etc/containers/certs.d/", cert.Name()),
+ User: getNodeUsr("root"),
},
- Mode: intToPtr(0644),
- },
- })
+ FileEmbedded1: FileEmbedded1{
+ Append: nil,
+ Contents: Resource{
+ Source: encodeDataURLPtr(string(b)),
+ },
+ Mode: intToPtr(0644),
+ },
+ })
+ }
+ } else {
+ if !os.IsNotExist(err) {
+ logrus.Warnf("Unable to copy certs via ignition, error while reading certs from %s: %s", certsDir, err.Error())
+ }
}
} else {
- if !os.IsNotExist(err) {
- logrus.Warnf("Unable to copy certs via ignition, error while reading certs from %s: %s", certsDir, err.Error())
+ fileName := filepath.Base(certsDir)
+ b, err := ioutil.ReadFile(certsDir)
+ if err != nil {
+ logrus.Warnf("Unable to read cert file %s", err.Error())
+ return files
}
+ files = append(files, File{
+ Node: Node{
+ Group: getNodeGrp("root"),
+ Path: filepath.Join("/etc/containers/certs.d/", fileName),
+ User: getNodeUsr("root"),
+ },
+ FileEmbedded1: FileEmbedded1{
+ Append: nil,
+ Contents: Resource{
+ Source: encodeDataURLPtr(string(b)),
+ },
+ Mode: intToPtr(0644),
+ },
+ })
}
return files
}
+func getProxyVariables() string {
+ proxyOpts := ""
+ for _, variable := range config.ProxyEnv {
+ if value, ok := os.LookupEnv(variable); ok {
+ proxyOpts += fmt.Sprintf("\n export %s=%s", variable, value)
+ }
+ }
+ return proxyOpts
+}
+
func getLinks(usrName string) []Link {
return []Link{{
Node: Node{
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index f09107c71..560037542 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -386,8 +386,16 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
}
if len(v.Mounts) > 0 {
- for !v.isRunning() || !v.isListening() {
+ running, err := v.isRunning()
+ if err != nil {
+ return err
+ }
+ for running || !v.isListening() {
time.Sleep(100 * time.Millisecond)
+ running, err = v.isRunning()
+ if err != nil {
+ return err
+ }
}
}
for _, mount := range v.Mounts {
@@ -416,8 +424,48 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
return nil
}
+func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.QemuMachineStatus, error) {
+ // this is the format returned from the monitor
+ // {"return": {"status": "running", "singlestep": false, "running": true}}
+
+ type statusDetails struct {
+ Status string `json:"status"`
+ Step bool `json:"singlestep"`
+ Running bool `json:"running"`
+ }
+ type statusResponse struct {
+ Response statusDetails `json:"return"`
+ }
+ var response statusResponse
+
+ checkCommand := struct {
+ Execute string `json:"execute"`
+ }{
+ Execute: "query-status",
+ }
+ input, err := json.Marshal(checkCommand)
+ if err != nil {
+ return "", err
+ }
+ b, err := monitor.Run(input)
+ if err != nil {
+ if errors.Cause(err) == os.ErrNotExist {
+ return machine.Stopped, nil
+ }
+ return "", err
+ }
+ if err := json.Unmarshal(b, &response); err != nil {
+ return "", err
+ }
+ if response.Response.Status == machine.Running {
+ return machine.Running, nil
+ }
+ return machine.Stopped, nil
+}
+
// Stop uses the qmp monitor to call a system_powerdown
func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
+ var disconnected bool
// check if the qmp socket is there. if not, qemu instance is gone
if _, err := os.Stat(v.QMPMonitor.Address); os.IsNotExist(err) {
// Right now it is NOT an error to stop a stopped machine
@@ -442,19 +490,22 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return err
}
defer func() {
- if err := qmpMonitor.Disconnect(); err != nil {
- logrus.Error(err)
+ if !disconnected {
+ if err := qmpMonitor.Disconnect(); err != nil {
+ logrus.Error(err)
+ }
}
}()
+
if _, err = qmpMonitor.Run(input); err != nil {
return err
}
+
qemuSocketFile, pidFile, err := v.getSocketandPid()
if err != nil {
return err
}
if _, err := os.Stat(pidFile); os.IsNotExist(err) {
- logrus.Info(err)
return nil
}
pidString, err := ioutil.ReadFile(pidFile)
@@ -483,6 +534,24 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return err
}
+ if err := qmpMonitor.Disconnect(); err != nil {
+ return nil
+ }
+
+ disconnected = true
+ waitInternal := 250 * time.Millisecond
+ for i := 0; i < 5; i++ {
+ running, err := v.isRunning()
+ if err != nil {
+ return err
+ }
+ if !running {
+ break
+ }
+ time.Sleep(waitInternal)
+ waitInternal = waitInternal * 2
+ }
+
return nil
}
@@ -519,7 +588,11 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun
)
// cannot remove a running vm
- if v.isRunning() {
+ running, err := v.isRunning()
+ if err != nil {
+ return "", nil, err
+ }
+ if running {
return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name)
}
@@ -578,16 +651,33 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun
}, nil
}
-func (v *MachineVM) isRunning() bool {
+func (v *MachineVM) isRunning() (bool, error) {
// Check if qmp socket path exists
if _, err := os.Stat(v.QMPMonitor.Address); os.IsNotExist(err) {
- return false
+ return false, nil
}
// Check if we can dial it
- if _, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address, v.QMPMonitor.Timeout); err != nil {
- return false
+ monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address, v.QMPMonitor.Timeout)
+ if err != nil {
+ return false, nil
}
- return true
+ if err := monitor.Connect(); err != nil {
+ return false, err
+ }
+ defer func() {
+ if err := monitor.Disconnect(); err != nil {
+ logrus.Error(err)
+ }
+ }()
+ // If there is a monitor, lets see if we can query state
+ state, err := v.checkStatus(monitor)
+ if err != nil {
+ return false, err
+ }
+ if state == machine.Running {
+ return true, nil
+ }
+ return false, nil
}
func (v *MachineVM) isListening() bool {
@@ -603,7 +693,11 @@ func (v *MachineVM) isListening() bool {
// SSH opens an interactive SSH session to the vm specified.
// Added ssh function to VM interface: pkg/machine/config/go : line 58
func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error {
- if !v.isRunning() {
+ running, err := v.isRunning()
+ if err != nil {
+ return err
+ }
+ if !running {
return errors.Errorf("vm %q is not running.", v.Name)
}
@@ -696,6 +790,9 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
listEntry.CPUs = vm.CPUs
listEntry.Memory = vm.Memory * units.MiB
listEntry.DiskSize = vm.DiskSize * units.GiB
+ listEntry.Port = vm.Port
+ listEntry.RemoteUsername = vm.RemoteUsername
+ listEntry.IdentityPath = vm.IdentityPath
fi, err := os.Stat(fullPath)
if err != nil {
return err
@@ -707,7 +804,11 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
return err
}
listEntry.LastUp = fi.ModTime()
- if vm.isRunning() {
+ running, err := vm.isRunning()
+ if err != nil {
+ return err
+ }
+ if running {
listEntry.Running = true
}