diff --git a/cmd/podman/system/service_abi.go b/cmd/podman/system/service_abi.go index d6b42ed294..339f093ec7 100644 --- a/cmd/podman/system/service_abi.go +++ b/cmd/podman/system/service_abi.go @@ -4,17 +4,18 @@ package system import ( - "context" + "fmt" "net" "net/url" "os" "path/filepath" + "github.com/containers/podman/v4/cmd/podman/registry" api "github.com/containers/podman/v4/pkg/api/server" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra" "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/sirupsen/logrus" "github.com/spf13/pflag" @@ -27,7 +28,26 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities 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) if err != nil { 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 { return err } - util.SetSocketPath(path) if os.Getenv("LISTEN_FDS") != "" { // If it is activated by systemd, use the first LISTEN_FD (3) // instead of opening the socket file. @@ -70,6 +89,7 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities default: logrus.Debugf("Attempting API Service endpoint scheme %q", uri.Scheme) } + libpodRuntime.SetRemoteURI(uri.String()) } // Close stdin, so shortnames will not prompt @@ -81,15 +101,10 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities if err := unix.Dup2(int(devNullfile.Fd()), int(os.Stdin.Fd())); err != nil { return err } - rt, err := infra.GetRuntime(context.Background(), flags, cfg) - if err != nil { - return err - } servicereaper.Start() - - infra.StartWatcher(rt) - server, err := api.NewServerWithSettings(rt, listener, opts) + infra.StartWatcher(libpodRuntime) + server, err := api.NewServerWithSettings(libpodRuntime, listener, opts) if err != nil { return err } diff --git a/libpod/runtime.go b/libpod/runtime.go index 07653217aa..28d0673bf3 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -1140,7 +1140,7 @@ func (r *Runtime) getVolumePlugin(name string) (*plugin.VolumePlugin, error) { 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 { return filepath.Join(r.store.GraphRoot(), "secrets") } @@ -1188,7 +1188,17 @@ func (r *Runtime) Network() nettypes.ContainerNetwork { 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 { 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 +} diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 73740a6f9f..0bccd81f9d 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,28 +64,13 @@ 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] - } + logrus.Infof("API service listening on %q. URI: %q", (*listener).Addr(), runtime.RemoteURI()) if opts.CorsHeaders == "" { logrus.Debug("CORS Headers were not set") } else { logrus.Debugf("CORS Headers were set to %q", opts.CorsHeaders) } - logrus.Infof("API service listening on %q", (*listener).Addr()) router := mux.NewRouter().UseEncodedPath() tracker := idle.NewTracker(opts.Timeout) diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index d12d14c1fa..56bafe5b11 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -28,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()} + + // `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 + } - socketPath, err := util.SocketPath() + 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/util/utils.go b/pkg/util/utils.go index 1beb3b28e3..0d4c035cbe 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -701,29 +701,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 { diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go index f989a9d299..3baa8f3feb 100644 --- a/test/e2e/info_test.go +++ b/test/e2e/info_test.go @@ -119,17 +119,30 @@ var _ = Describe("Podman Info", func() { Expect(string(out)).To(Equal(expect)) }) - It("podman info check RemoteSocket", func() { + It("check RemoteSocket w/ no server", func() { + SkipIfRemote("Verify RemoteSocket when no API server running") session := podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Path}}"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(MatchRegexp("/run/.*podman.*sock")) + session = podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Exists}}"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal("false")) + }) + + It("check RemoteSocket w/ remote server", func() { if IsRemote() { + session := podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Path}}"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(podmanTest.RemoteSocket)) + session = podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Exists}}"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(ContainSubstring("true")) + Expect(session.OutputToString()).To(Equal("true")) } }) @@ -139,13 +152,13 @@ var _ = Describe("Podman Info", func() { Expect(session).To(Exit(0)) if podmanTest.RemoteTest { - Expect(session.OutputToString()).To(ContainSubstring("true")) + Expect(session.OutputToString()).To(Equal("true")) } else { - Expect(session.OutputToString()).To(ContainSubstring("false")) + Expect(session.OutputToString()).To(Equal("false")) } }) - 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") SkipIfRootlessCgroupsV1("Disable cgroups not supported on cgroupv1 for rootless users") session := podmanTest.Podman([]string{"info", "--format", "{{.Host.CgroupControllers}}"}) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 6a40835653..9763ad9afd 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -3149,6 +3149,9 @@ invalid kube kind Expect(ls.OutputToStringArray()).To(HaveLen(1)) containerLen := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "'{{len .Containers}}'"}) + containerLen.WaitWithDefaultTimeout() + Expect(ls).Should(Exit(0)) + Expect(ls.OutputToStringArray()).Should(BeNumerically(">", 1)) ctr01Name := "ctr01" ctr02Name := "ctr02" diff --git a/test/e2e/system_connection_test.go b/test/e2e/system_connection_test.go index ac4c5e5eab..9cb49acb4a 100644 --- a/test/e2e/system_connection_test.go +++ b/test/e2e/system_connection_test.go @@ -247,7 +247,7 @@ var _ = Describe("podman system connection", func() { // podman-remote commands will be executed by ginkgo directly. SkipIfContainerized("sshd is not available when running in a container") 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") SkipIfNotActive("sshd", "cannot test connection heuristic if sshd is not running") }) diff --git a/test/e2e/system_service_test.go b/test/e2e/system_service_test.go index 2bc7756d60..125dd35e2c 100644 --- a/test/e2e/system_service_test.go +++ b/test/e2e/system_service_test.go @@ -20,7 +20,7 @@ var _ = Describe("podman system service", func() { // The timeout used to for the service to respond. As shown in #12167, // this may take some time on machines under high load. - var timeout = 20 + var timeout = 30 BeforeEach(func() { tempdir, err := CreateTempDirInTempDir() diff --git a/test/system/272-system-connection.bats b/test/system/272-system-connection.bats index 7b70f60f47..7ad6a4e496 100644 --- a/test/system/272-system-connection.bats +++ b/test/system/272-system-connection.bats @@ -99,10 +99,9 @@ $c2[ ]\+tcp://localhost:54321[ ]\+true" \ _SERVICE_PID=$! wait_for_port localhost $_SERVICE_PORT - # FIXME: #12023, RemoteSocket is always /run/something -# run_podman info --format '{{.Host.RemoteSocket.Path}}' -# is "$output" "tcp:localhost:$_SERVICE_PORT" \ -# "podman info works, and talks to the correct server" + run_podman info --format '{{.Host.RemoteSocket.Path}}' + is "$output" "tcp:localhost:$_SERVICE_PORT" \ + "podman info works, and talks to the correct server" _run_podman_remote info --format '{{.Store.GraphRoot}}' is "$output" "${PODMAN_TMPDIR}/root" \