Report correct RemoteURI

Rather than assuming a filesystem path, the API service URI is recorded
in the libpod runtime configuration and then reported as requested.

Note: All schemes other than "unix" are hard-coded to report URI exists.

Fixes #12023

Signed-off-by: Jhon Honce <jhonce@redhat.com>
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh 2022-04-27 15:48:04 -04:00
parent b1e9ea38e5
commit 5fa6f686db
No known key found for this signature in database
GPG Key ID: A2DF901DABE2C028
10 changed files with 92 additions and 96 deletions

View File

@ -4,17 +4,18 @@
package system package system
import ( import (
"context" "fmt"
"net" "net"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"github.com/containers/podman/v4/cmd/podman/registry"
api "github.com/containers/podman/v4/pkg/api/server" api "github.com/containers/podman/v4/pkg/api/server"
"github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/domain/infra" "github.com/containers/podman/v4/pkg/domain/infra"
"github.com/containers/podman/v4/pkg/servicereaper" "github.com/containers/podman/v4/pkg/servicereaper"
"github.com/containers/podman/v4/pkg/util" "github.com/coreos/go-systemd/v22/activation"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@ -27,7 +28,26 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
err error err error
) )
if opts.URI != "" { libpodRuntime, err := infra.GetRuntime(registry.Context(), flags, cfg)
if err != nil {
return err
}
if opts.URI == "" {
if _, found := os.LookupEnv("LISTEN_PID"); !found {
return errors.New("no service URI provided and socket activation protocol is not active")
}
listeners, err := activation.Listeners()
if err != nil {
return fmt.Errorf("cannot retrieve file descriptors from systemd: %w", err)
}
if len(listeners) != 1 {
return fmt.Errorf("wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners))
}
listener = listeners[0]
libpodRuntime.SetRemoteURI(listeners[0].Addr().String())
} else {
uri, err := url.Parse(opts.URI) uri, err := url.Parse(opts.URI)
if err != nil { if err != nil {
return errors.Errorf("%s is an invalid socket destination", opts.URI) return errors.Errorf("%s is an invalid socket destination", opts.URI)
@ -39,7 +59,6 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
if err != nil { if err != nil {
return err return err
} }
util.SetSocketPath(path)
if os.Getenv("LISTEN_FDS") != "" { if os.Getenv("LISTEN_FDS") != "" {
// If it is activated by systemd, use the first LISTEN_FD (3) // If it is activated by systemd, use the first LISTEN_FD (3)
// instead of opening the socket file. // instead of opening the socket file.
@ -67,6 +86,7 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
default: default:
logrus.Debugf("Attempting API Service endpoint scheme %q", uri.Scheme) logrus.Debugf("Attempting API Service endpoint scheme %q", uri.Scheme)
} }
libpodRuntime.SetRemoteURI(uri.String())
} }
// Close stdin, so shortnames will not prompt // Close stdin, so shortnames will not prompt
@ -78,15 +98,10 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
if err := unix.Dup2(int(devNullfile.Fd()), int(os.Stdin.Fd())); err != nil { if err := unix.Dup2(int(devNullfile.Fd()), int(os.Stdin.Fd())); err != nil {
return err return err
} }
rt, err := infra.GetRuntime(context.Background(), flags, cfg)
if err != nil {
return err
}
servicereaper.Start() servicereaper.Start()
infra.StartWatcher(libpodRuntime)
infra.StartWatcher(rt) server, err := api.NewServerWithSettings(libpodRuntime, listener, opts)
server, err := api.NewServerWithSettings(rt, listener, opts)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1158,7 +1158,7 @@ func (r *Runtime) getVolumePlugin(name string) (*plugin.VolumePlugin, error) {
return plugin.GetVolumePlugin(name, pluginPath) return plugin.GetVolumePlugin(name, pluginPath)
} }
// GetSecretsStoreageDir returns the directory that the secrets manager should take // GetSecretsStorageDir returns the directory that the secrets manager should take
func (r *Runtime) GetSecretsStorageDir() string { func (r *Runtime) GetSecretsStorageDir() string {
return filepath.Join(r.store.GraphRoot(), "secrets") return filepath.Join(r.store.GraphRoot(), "secrets")
} }
@ -1206,7 +1206,17 @@ func (r *Runtime) Network() nettypes.ContainerNetwork {
return r.network return r.network
} }
// Network returns the network interface which is used by the runtime // GetDefaultNetworkName returns the network interface which is used by the runtime
func (r *Runtime) GetDefaultNetworkName() string { func (r *Runtime) GetDefaultNetworkName() string {
return r.config.Network.DefaultNetwork return r.config.Network.DefaultNetwork
} }
// RemoteURI returns the API server URI
func (r *Runtime) RemoteURI() string {
return r.config.Engine.RemoteURI
}
// SetRemoteURI records the API server URI
func (r *Runtime) SetRemoteURI(uri string) {
r.config.Engine.RemoteURI = uri
}

View File

@ -20,7 +20,6 @@ import (
"github.com/containers/podman/v4/pkg/api/server/idle" "github.com/containers/podman/v4/pkg/api/server/idle"
"github.com/containers/podman/v4/pkg/api/types" "github.com/containers/podman/v4/pkg/api/types"
"github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/coreos/go-systemd/v22/activation"
"github.com/coreos/go-systemd/v22/daemon" "github.com/coreos/go-systemd/v22/daemon"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/schema" "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) { func newServer(runtime *libpod.Runtime, listener net.Listener, opts entities.ServiceOptions) (*APIServer, error) {
// If listener not provided try socket activation protocol logrus.Infof("API service listening on %q. URI: %q", listener.Addr(), runtime.RemoteURI())
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")
}
}
if opts.CorsHeaders == "" { if opts.CorsHeaders == "" {
logrus.Debug("CORS Headers were not set") logrus.Debug("CORS Headers were not set")
} else { } else {

View File

@ -6,6 +6,7 @@ import (
"net/url" "net/url"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/cgroups"
"github.com/containers/common/pkg/config" "github.com/containers/common/pkg/config"
@ -27,27 +28,40 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
if err != nil { if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
rs := define.RemoteSocket{
Path: socketPath, if uri.Scheme == "unix" {
Exists: false, _, err := os.Stat(uri.Path)
info.Host.RemoteSocket.Exists = err == nil
} else {
info.Host.RemoteSocket.Exists = true
} }
// Check if the socket exists
if fi, err := os.Stat(socketPath); err == nil {
if fi.Mode()&os.ModeSocket != 0 {
rs.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 return info, err
} }

View File

@ -731,29 +731,6 @@ func IDtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxI
return convertedIDMap 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) { func LookupUser(name string) (*user.User, error) {
// Assume UID look up first, if it fails lookup by username // Assume UID look up first, if it fails lookup by username
if u, err := user.LookupId(name); err == nil { if u, err := user.LookupId(name); err == nil {

View File

@ -119,33 +119,31 @@ var _ = Describe("Podman Info", func() {
Expect(string(out)).To(Equal(expect)) Expect(string(out)).To(Equal(expect))
}) })
It("podman info check RemoteSocket", func() { It("check RemoteSocket ", func() {
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Path}}"}) session := podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Path}}"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0)) Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(MatchRegexp("/run/.*podman.*sock")) Expect(session.OutputToString()).To(MatchRegexp("/run/.*podman.*sock"))
if IsRemote() { session = podmanTest.Podman([]string{"info", "--format", "{{.Host.ServiceIsRemote}}"})
session = podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Exists}}"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("true"))
}
})
It("verify ServiceIsRemote", func() {
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.ServiceIsRemote}}"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0)) Expect(session).Should(Exit(0))
if podmanTest.RemoteTest { if podmanTest.RemoteTest {
Expect(session.OutputToString()).To(ContainSubstring("true")) Expect(session.OutputToString()).To(Equal("true"))
} else { } else {
Expect(session.OutputToString()).To(ContainSubstring("false")) Expect(session.OutputToString()).To(Equal("false"))
} }
session = podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Exists}}"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
if IsRemote() {
Expect(session.OutputToString()).To(ContainSubstring("true"))
}
}) })
It("Podman info must contain cgroupControllers with ReleventControllers", func() { It("Podman info must contain cgroupControllers with RelevantControllers", func() {
SkipIfRootless("Hard to tell which controllers are going to be enabled for rootless") SkipIfRootless("Hard to tell which controllers are going to be enabled for rootless")
SkipIfRootlessCgroupsV1("Disable cgroups not supported on cgroupv1 for rootless users") SkipIfRootlessCgroupsV1("Disable cgroups not supported on cgroupv1 for rootless users")
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.CgroupControllers}}"}) session := podmanTest.Podman([]string{"info", "--format", "{{.Host.CgroupControllers}}"})

View File

@ -3180,8 +3180,10 @@ invalid kube kind
Expect(ls).Should(Exit(0)) Expect(ls).Should(Exit(0))
Expect(ls.OutputToStringArray()).To(HaveLen(1)) Expect(ls.OutputToStringArray()).To(HaveLen(1))
containerLen := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "'{{len .Containers}}'"}) containerLen := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "{{len .Containers}}"})
containerLen.WaitWithDefaultTimeout()
Expect(containerLen).Should(Exit(0))
Expect(containerLen.OutputToString()).To(Equal("2"))
ctr01Name := "ctr01" ctr01Name := "ctr01"
ctr02Name := "ctr02" ctr02Name := "ctr02"
@ -3199,7 +3201,7 @@ invalid kube kind
replace.WaitWithDefaultTimeout() replace.WaitWithDefaultTimeout()
Expect(replace).Should(Exit(0)) Expect(replace).Should(Exit(0))
newContainerLen := podmanTest.Podman([]string{"pod", "inspect", newPod.Name, "--format", "'{{len .Containers}}'"}) newContainerLen := podmanTest.Podman([]string{"pod", "inspect", newPod.Name, "--format", "{{len .Containers}}"})
newContainerLen.WaitWithDefaultTimeout() newContainerLen.WaitWithDefaultTimeout()
Expect(newContainerLen).Should(Exit(0)) Expect(newContainerLen).Should(Exit(0))
Expect(newContainerLen.OutputToString()).NotTo(Equal(containerLen.OutputToString())) Expect(newContainerLen.OutputToString()).NotTo(Equal(containerLen.OutputToString()))

View File

@ -247,7 +247,7 @@ var _ = Describe("podman system connection", func() {
// podman-remote commands will be executed by ginkgo directly. // podman-remote commands will be executed by ginkgo directly.
SkipIfContainerized("sshd is not available when running in a container") SkipIfContainerized("sshd is not available when running in a container")
SkipIfRemote("connection heuristic requires both podman and podman-remote binaries") SkipIfRemote("connection heuristic requires both podman and podman-remote binaries")
SkipIfNotRootless("FIXME: setup ssh keys when root") SkipIfNotRootless(fmt.Sprintf("FIXME: setup ssh keys when root. uid(%d) euid(%d)", os.Getuid(), os.Geteuid()))
SkipIfSystemdNotRunning("cannot test connection heuristic if systemd is not running") SkipIfSystemdNotRunning("cannot test connection heuristic if systemd is not running")
SkipIfNotActive("sshd", "cannot test connection heuristic if sshd is not running") SkipIfNotActive("sshd", "cannot test connection heuristic if sshd is not running")
}) })

View File

@ -20,7 +20,7 @@ var _ = Describe("podman system service", func() {
// The timeout used to for the service to respond. As shown in #12167, // The timeout used to for the service to respond. As shown in #12167,
// this may take some time on machines under high load. // this may take some time on machines under high load.
var timeout = 20 var timeout = 30
BeforeEach(func() { BeforeEach(func() {
tempdir, err := CreateTempDirInTempDir() tempdir, err := CreateTempDirInTempDir()

View File

@ -99,10 +99,9 @@ $c2[ ]\+tcp://localhost:54321[ ]\+true" \
_SERVICE_PID=$! _SERVICE_PID=$!
wait_for_port localhost $_SERVICE_PORT wait_for_port localhost $_SERVICE_PORT
# FIXME: #12023, RemoteSocket is always /run/something _run_podman_remote info --format '{{.Host.RemoteSocket.Path}}'
# run_podman info --format '{{.Host.RemoteSocket.Path}}' is "$output" "tcp:localhost:$_SERVICE_PORT" \
# is "$output" "tcp:localhost:$_SERVICE_PORT" \ "podman info works, and talks to the correct server"
# "podman info works, and talks to the correct server"
_run_podman_remote info --format '{{.Store.GraphRoot}}' _run_podman_remote info --format '{{.Store.GraphRoot}}'
is "$output" "${PODMAN_TMPDIR}/root" \ is "$output" "${PODMAN_TMPDIR}/root" \