Implement publishing API UNIX socket on Windows platforms

gvproxy and win-sshproxy have capabilities to serve this type of enpoint.
This change only adds one additional API enpoint publishing by appending
proxy command lines.

Signed-off-by: Arthur Sengileyev <arthur.sengileyev@gmail.com>
This commit is contained in:
Arthur Sengileyev
2024-07-25 18:27:49 +03:00
parent ece759a69a
commit 1732338630
8 changed files with 145 additions and 17 deletions

View File

@@ -0,0 +1,96 @@
package e2e_test
import (
"context"
"os/exec"
"path/filepath"
"runtime"
"github.com/containers/podman/v5/pkg/machine"
"github.com/docker/docker/client"
jsoniter "github.com/json-iterator/go"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
)
const (
NamedPipeProto = "npipe://"
)
var _ = Describe("run podman API test calls", func() {
It("client connect to machine socket", func() {
if runtime.GOOS == "windows" {
Skip("Go docker client doesn't support unix socket on Windows")
}
name := randomString()
i := new(initMachine)
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow()).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))
inspectJSON := new(inspectMachine)
inspectSession, err := mb.setName(name).setCmd(inspectJSON).run()
Expect(err).ToNot(HaveOccurred())
Expect(inspectSession).To(Exit(0))
var inspectInfo []machine.InspectInfo
err = jsoniter.Unmarshal(inspectSession.Bytes(), &inspectInfo)
Expect(err).ToNot(HaveOccurred())
sockPath := inspectInfo[0].ConnectionInfo.PodmanSocket.GetPath()
cli, err := client.NewClientWithOpts(client.WithHost("unix://" + sockPath))
Expect(err).ToNot(HaveOccurred())
_, err = cli.Ping(context.Background())
Expect(err).ToNot(HaveOccurred())
})
It("client connect to machine named pipe", func() {
if runtime.GOOS != "windows" {
Skip("test is only supported on Windows")
}
name := randomString()
i := new(initMachine)
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow()).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))
inspectJSON := new(inspectMachine)
inspectSession, err := mb.setName(name).setCmd(inspectJSON).run()
Expect(err).ToNot(HaveOccurred())
Expect(inspectSession).To(Exit(0))
var inspectInfo []machine.InspectInfo
err = jsoniter.Unmarshal(inspectSession.Bytes(), &inspectInfo)
Expect(err).ToNot(HaveOccurred())
pipePath := inspectInfo[0].ConnectionInfo.PodmanPipe.GetPath()
cli, err := client.NewClientWithOpts(client.WithHost(NamedPipeProto + filepath.ToSlash(pipePath)))
Expect(err).ToNot(HaveOccurred())
_, err = cli.Ping(context.Background())
Expect(err).ToNot(HaveOccurred())
})
It("curl connect to machine socket", func() {
name := randomString()
i := new(initMachine)
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow()).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))
inspectJSON := new(inspectMachine)
inspectSession, err := mb.setName(name).setCmd(inspectJSON).run()
Expect(err).ToNot(HaveOccurred())
Expect(inspectSession).To(Exit(0))
var inspectInfo []machine.InspectInfo
err = jsoniter.Unmarshal(inspectSession.Bytes(), &inspectInfo)
Expect(err).ToNot(HaveOccurred())
sockPath := inspectInfo[0].ConnectionInfo.PodmanSocket.GetPath()
cmd := exec.Command("curl", "--unix-socket", sockPath, "http://d/v5.0.0/libpod/info")
err = cmd.Run()
Expect(err).ToNot(HaveOccurred())
})
})

View File

@@ -80,9 +80,6 @@ var _ = Describe("podman inspect stop", func() {
}) })
It("inspect shows a unique socket name per machine", func() { It("inspect shows a unique socket name per machine", func() {
skipIfVmtype(define.WSLVirt, "test is only relevant for Unix based providers")
skipIfVmtype(define.HyperVVirt, "test is only relevant for Unix based machines")
var socks []string var socks []string
for c := 0; c < 2; c++ { for c := 0; c < 2; c++ {
name := randomString() name := randomString()

View File

@@ -18,6 +18,7 @@ import (
winio "github.com/Microsoft/go-winio" winio "github.com/Microsoft/go-winio"
"github.com/containers/podman/v5/pkg/machine/define" "github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/env" "github.com/containers/podman/v5/pkg/machine/env"
"github.com/containers/podman/v5/pkg/machine/sockets"
"github.com/containers/storage/pkg/fileutils" "github.com/containers/storage/pkg/fileutils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@@ -45,6 +46,7 @@ type WinProxyOpts struct {
RemoteUsername string RemoteUsername string
Rootful bool Rootful bool
VMType define.VMType VMType define.VMType
Socket *define.VMFile
} }
func GetProcessState(pid int) (active bool, exitCode int) { func GetProcessState(pid int) (active bool, exitCode int) {
@@ -160,6 +162,12 @@ func launchWinProxy(opts WinProxyOpts) (bool, string, error) {
waitPipe = GlobalNamedPipe waitPipe = GlobalNamedPipe
} }
hostURL, err := sockets.ToUnixURL(opts.Socket)
if err != nil {
return false, "", err
}
args = append(args, hostURL.String(), dest, opts.IdentityPath)
cmd := exec.Command(command, args...) cmd := exec.Command(command, args...)
logrus.Debugf("winssh command: %s %v", command, args) logrus.Debugf("winssh command: %s %v", command, args)
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {

View File

@@ -6,6 +6,7 @@ import (
"github.com/containers/podman/v5/pkg/machine" "github.com/containers/podman/v5/pkg/machine"
"github.com/containers/podman/v5/pkg/machine/define" "github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/env" "github.com/containers/podman/v5/pkg/machine/env"
sc "github.com/containers/podman/v5/pkg/machine/sockets"
"github.com/containers/podman/v5/pkg/machine/vmconfigs" "github.com/containers/podman/v5/pkg/machine/vmconfigs"
) )
@@ -22,5 +23,16 @@ func setupMachineSockets(mc *vmconfigs.MachineConfig, dirs *define.MachineDirs)
state = machine.DockerGlobal state = machine.DockerGlobal
} }
return sockets, sockets[len(sockets)-1], state, nil hostSocket, err := mc.APISocket()
if err != nil {
return nil, "", 0, err
}
hostURL, err := sc.ToUnixURL(hostSocket)
if err != nil {
return nil, "", 0, err
}
sockets = append(sockets, hostURL.String())
return sockets, sockets[len(sockets)-2], state, nil
} }

View File

@@ -15,7 +15,6 @@ import (
"github.com/containers/podman/v5/pkg/errorhandling" "github.com/containers/podman/v5/pkg/errorhandling"
"github.com/containers/podman/v5/pkg/machine/connection" "github.com/containers/podman/v5/pkg/machine/connection"
"github.com/containers/podman/v5/pkg/machine/define" "github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/env"
"github.com/containers/podman/v5/pkg/machine/lock" "github.com/containers/podman/v5/pkg/machine/lock"
"github.com/containers/podman/v5/pkg/machine/ports" "github.com/containers/podman/v5/pkg/machine/ports"
"github.com/containers/storage/pkg/fileutils" "github.com/containers/storage/pkg/fileutils"
@@ -331,19 +330,8 @@ func (mc *MachineConfig) IsFirstBoot() (bool, error) {
} }
func (mc *MachineConfig) ConnectionInfo(vmtype define.VMType) (*define.VMFile, *define.VMFile, error) { func (mc *MachineConfig) ConnectionInfo(vmtype define.VMType) (*define.VMFile, *define.VMFile, error) {
var (
socket *define.VMFile
pipe *define.VMFile
)
if vmtype == define.HyperVVirt || vmtype == define.WSLVirt {
pipeName := env.WithPodmanPrefix(mc.Name)
pipe = &define.VMFile{Path: `\\.\pipe\` + pipeName}
return nil, pipe, nil
}
socket, err := mc.APISocket() socket, err := mc.APISocket()
return socket, nil, err return socket, getPipe(mc.Name), err
} }
// LoadMachineByName returns a machine config based on the vm name and provider // LoadMachineByName returns a machine config based on the vm name and provider

View File

@@ -0,0 +1,11 @@
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
package vmconfigs
import (
"github.com/containers/podman/v5/pkg/machine/define"
)
func getPipe(name string) *define.VMFile {
return nil
}

View File

@@ -0,0 +1,11 @@
package vmconfigs
import (
"github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/env"
)
func getPipe(name string) *define.VMFile {
pipeName := env.WithPodmanPrefix(name)
return &define.VMFile{Path: `\\.\pipe\` + pipeName}
}

View File

@@ -191,6 +191,10 @@ func (w WSLStubber) RequireExclusiveActive() bool {
} }
func (w WSLStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool) error { func (w WSLStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool) error {
socket, err := mc.APISocket()
if err != nil {
return err
}
winProxyOpts := machine.WinProxyOpts{ winProxyOpts := machine.WinProxyOpts{
Name: mc.Name, Name: mc.Name,
IdentityPath: mc.SSH.IdentityPath, IdentityPath: mc.SSH.IdentityPath,
@@ -198,6 +202,7 @@ func (w WSLStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool
RemoteUsername: mc.SSH.RemoteUsername, RemoteUsername: mc.SSH.RemoteUsername,
Rootful: mc.HostUser.Rootful, Rootful: mc.HostUser.Rootful,
VMType: w.VMType(), VMType: w.VMType(),
Socket: socket,
} }
machine.LaunchWinProxy(winProxyOpts, noInfo) machine.LaunchWinProxy(winProxyOpts, noInfo)