github.com/containers/podman/v4@v4.9.4/test/e2e/system_connection_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"os"
     7  	"os/exec"
     8  	"os/user"
     9  	"path/filepath"
    10  
    11  	"github.com/containers/common/pkg/config"
    12  	. "github.com/containers/podman/v4/test/utils"
    13  	. "github.com/onsi/ginkgo/v2"
    14  	. "github.com/onsi/gomega"
    15  	. "github.com/onsi/gomega/gbytes"
    16  	. "github.com/onsi/gomega/gexec"
    17  )
    18  
    19  func setupEmptyContainersConf() {
    20  	// make sure connections are not written to real user config on host
    21  	file := filepath.Join(podmanTest.TempDir, "containersconf")
    22  	f, err := os.Create(file)
    23  	Expect(err).ToNot(HaveOccurred())
    24  	f.Close()
    25  	os.Setenv("CONTAINERS_CONF", file)
    26  }
    27  
    28  var _ = Describe("podman system connection", func() {
    29  
    30  	BeforeEach(setupEmptyContainersConf)
    31  
    32  	Context("without running API service", func() {
    33  		It("add ssh://", func() {
    34  			cmd := []string{"system", "connection", "add",
    35  				"--default",
    36  				"--identity", "~/.ssh/id_rsa",
    37  				"QA",
    38  				"ssh://root@podman.test:2222/run/podman/podman.sock",
    39  			}
    40  			session := podmanTest.Podman(cmd)
    41  			session.WaitWithDefaultTimeout()
    42  			Expect(session).Should(ExitCleanly())
    43  			Expect(session.Out.Contents()).Should(BeEmpty())
    44  
    45  			cfg, err := config.ReadCustomConfig()
    46  			Expect(err).ShouldNot(HaveOccurred())
    47  			Expect(cfg).Should(HaveActiveService("QA"))
    48  			Expect(cfg).Should(VerifyService(
    49  				"QA",
    50  				"ssh://root@podman.test:2222/run/podman/podman.sock",
    51  				"~/.ssh/id_rsa",
    52  			))
    53  
    54  			cmd = []string{"system", "connection", "rename",
    55  				"QA",
    56  				"QE",
    57  			}
    58  			session = podmanTest.Podman(cmd)
    59  			session.WaitWithDefaultTimeout()
    60  			Expect(session).Should(ExitCleanly())
    61  
    62  			Expect(config.ReadCustomConfig()).Should(HaveActiveService("QE"))
    63  		})
    64  
    65  		It("add UDS", func() {
    66  			cmd := []string{"system", "connection", "add",
    67  				"QA-UDS",
    68  				"unix:///run/podman/podman.sock",
    69  			}
    70  			session := podmanTest.Podman(cmd)
    71  			session.WaitWithDefaultTimeout()
    72  			Expect(session).Should(Exit(0))
    73  			Expect(session.Out.Contents()).Should(BeEmpty())
    74  			// stderr will probably warn (ENOENT or EACCESS) about socket
    75  			// but it's too unreliable to test for.
    76  
    77  			Expect(config.ReadCustomConfig()).Should(VerifyService(
    78  				"QA-UDS",
    79  				"unix:///run/podman/podman.sock",
    80  				"",
    81  			))
    82  
    83  			cmd = []string{"system", "connection", "add",
    84  				"QA-UDS1",
    85  				"--socket-path", "/run/user/podman/podman.sock",
    86  				"unix:///run/podman/podman.sock",
    87  			}
    88  			session = podmanTest.Podman(cmd)
    89  			session.WaitWithDefaultTimeout()
    90  			Expect(session).Should(Exit(0))
    91  			Expect(session.Out.Contents()).Should(BeEmpty())
    92  
    93  			Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA-UDS"))
    94  			Expect(config.ReadCustomConfig()).Should(VerifyService(
    95  				"QA-UDS1",
    96  				"unix:///run/user/podman/podman.sock",
    97  				"",
    98  			))
    99  		})
   100  
   101  		It("add tcp", func() {
   102  			cmd := []string{"system", "connection", "add",
   103  				"QA-TCP",
   104  				"tcp://localhost:8888",
   105  			}
   106  			session := podmanTest.Podman(cmd)
   107  			session.WaitWithDefaultTimeout()
   108  			Expect(session).Should(ExitCleanly())
   109  			Expect(session.Out.Contents()).Should(BeEmpty())
   110  
   111  			Expect(config.ReadCustomConfig()).Should(VerifyService(
   112  				"QA-TCP",
   113  				"tcp://localhost:8888",
   114  				"",
   115  			))
   116  		})
   117  
   118  		It("add to new farm", func() {
   119  			cfg, err := config.ReadCustomConfig()
   120  			Expect(err).ShouldNot(HaveOccurred())
   121  			Expect(cfg.Farms.List).Should(BeEmpty())
   122  
   123  			cmd := []string{"system", "connection", "add",
   124  				"--default",
   125  				"--identity", "~/.ssh/id_rsa",
   126  				"--farm", "farm1",
   127  				"QA",
   128  				"ssh://root@podman.test:2222/run/podman/podman.sock",
   129  			}
   130  			session := podmanTest.Podman(cmd)
   131  			session.WaitWithDefaultTimeout()
   132  			Expect(session).Should(ExitCleanly())
   133  			Expect(session.Out.Contents()).Should(BeEmpty())
   134  
   135  			cfg, err = config.ReadCustomConfig()
   136  			Expect(err).ShouldNot(HaveOccurred())
   137  			Expect(cfg).Should(HaveActiveService("QA"))
   138  			Expect(cfg).Should(VerifyService(
   139  				"QA",
   140  				"ssh://root@podman.test:2222/run/podman/podman.sock",
   141  				"~/.ssh/id_rsa",
   142  			))
   143  			Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA"}))
   144  		})
   145  
   146  		It("add to existing farm", func() {
   147  			// create empty farm
   148  			cmd := []string{"farm", "create", "empty-farm"}
   149  			session := podmanTest.Podman(cmd)
   150  			session.WaitWithDefaultTimeout()
   151  			Expect(session).Should(ExitCleanly())
   152  			Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"empty-farm\" created"))
   153  
   154  			cfg, err := config.ReadCustomConfig()
   155  			Expect(err).ShouldNot(HaveOccurred())
   156  			Expect(cfg.Farms.List).Should(HaveKeyWithValue("empty-farm", []string{}))
   157  
   158  			cmd = []string{"system", "connection", "add",
   159  				"--default",
   160  				"--identity", "~/.ssh/id_rsa",
   161  				"--farm", "empty-farm",
   162  				"QA",
   163  				"ssh://root@podman.test:2222/run/podman/podman.sock",
   164  			}
   165  			session = podmanTest.Podman(cmd)
   166  			session.WaitWithDefaultTimeout()
   167  			Expect(session).Should(ExitCleanly())
   168  			Expect(session.Out.Contents()).Should(BeEmpty())
   169  
   170  			cfg, err = config.ReadCustomConfig()
   171  			Expect(err).ShouldNot(HaveOccurred())
   172  			Expect(cfg).Should(HaveActiveService("QA"))
   173  			Expect(cfg).Should(VerifyService(
   174  				"QA",
   175  				"ssh://root@podman.test:2222/run/podman/podman.sock",
   176  				"~/.ssh/id_rsa",
   177  			))
   178  			Expect(cfg.Farms.List).Should(HaveKeyWithValue("empty-farm", []string{"QA"}))
   179  		})
   180  
   181  		It("removing connection should remove from farm also", func() {
   182  			cfg, err := config.ReadCustomConfig()
   183  			Expect(err).ShouldNot(HaveOccurred())
   184  			Expect(cfg.Farms.List).Should(BeEmpty())
   185  
   186  			cmd := []string{"system", "connection", "add",
   187  				"--default",
   188  				"--identity", "~/.ssh/id_rsa",
   189  				"--farm", "farm1",
   190  				"QA",
   191  				"ssh://root@podman.test:2222/run/podman/podman.sock",
   192  			}
   193  			session := podmanTest.Podman(cmd)
   194  			session.WaitWithDefaultTimeout()
   195  			Expect(session).Should(ExitCleanly())
   196  			Expect(session.Out.Contents()).Should(BeEmpty())
   197  
   198  			cfg, err = config.ReadCustomConfig()
   199  			Expect(err).ShouldNot(HaveOccurred())
   200  			Expect(cfg).Should(HaveActiveService("QA"))
   201  			Expect(cfg).Should(VerifyService(
   202  				"QA",
   203  				"ssh://root@podman.test:2222/run/podman/podman.sock",
   204  				"~/.ssh/id_rsa",
   205  			))
   206  			Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA"}))
   207  
   208  			// Remove the QA connection
   209  			session = podmanTest.Podman([]string{"system", "connection", "remove", "QA"})
   210  			session.WaitWithDefaultTimeout()
   211  			Expect(session).Should(ExitCleanly())
   212  			Expect(session.Out.Contents()).Should(BeEmpty())
   213  
   214  			cfg, err = config.ReadCustomConfig()
   215  			Expect(err).ShouldNot(HaveOccurred())
   216  			Expect(cfg.Engine.ActiveService).Should(BeEmpty())
   217  			Expect(cfg.Engine.ServiceDestinations).Should(BeEmpty())
   218  			Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{}))
   219  		})
   220  
   221  		It("remove", func() {
   222  			session := podmanTest.Podman([]string{"system", "connection", "add",
   223  				"--default",
   224  				"--identity", "~/.ssh/id_rsa",
   225  				"QA",
   226  				"ssh://root@podman.test:2222/run/podman/podman.sock",
   227  			})
   228  			session.WaitWithDefaultTimeout()
   229  			Expect(session).Should(ExitCleanly())
   230  
   231  			// two passes to test that removing non-existent connection is not an error
   232  			for i := 0; i < 2; i++ {
   233  				session = podmanTest.Podman([]string{"system", "connection", "remove", "QA"})
   234  				session.WaitWithDefaultTimeout()
   235  				Expect(session).Should(ExitCleanly())
   236  				Expect(session.Out.Contents()).Should(BeEmpty())
   237  
   238  				cfg, err := config.ReadCustomConfig()
   239  				Expect(err).ShouldNot(HaveOccurred())
   240  				Expect(cfg.Engine.ActiveService).Should(BeEmpty())
   241  				Expect(cfg.Engine.ServiceDestinations).Should(BeEmpty())
   242  			}
   243  		})
   244  
   245  		It("remove --all", func() {
   246  			session := podmanTest.Podman([]string{"system", "connection", "add",
   247  				"--default",
   248  				"--identity", "~/.ssh/id_rsa",
   249  				"QA",
   250  				"ssh://root@podman.test:2222/run/podman/podman.sock",
   251  			})
   252  			session.WaitWithDefaultTimeout()
   253  			Expect(session).Should(ExitCleanly())
   254  
   255  			session = podmanTest.Podman([]string{"system", "connection", "remove", "--all"})
   256  			session.WaitWithDefaultTimeout()
   257  			Expect(session).Should(ExitCleanly())
   258  			Expect(session.Out.Contents()).Should(BeEmpty())
   259  			Expect(session.Err.Contents()).Should(BeEmpty())
   260  
   261  			session = podmanTest.Podman([]string{"system", "connection", "list"})
   262  			session.WaitWithDefaultTimeout()
   263  			Expect(session).Should(ExitCleanly())
   264  		})
   265  
   266  		It("default", func() {
   267  			for _, name := range []string{"devl", "qe"} {
   268  				cmd := []string{"system", "connection", "add",
   269  					"--default",
   270  					"--identity", "~/.ssh/id_rsa",
   271  					name,
   272  					"ssh://root@podman.test:2222/run/podman/podman.sock",
   273  				}
   274  				session := podmanTest.Podman(cmd)
   275  				session.WaitWithDefaultTimeout()
   276  				Expect(session).Should(ExitCleanly())
   277  			}
   278  
   279  			cmd := []string{"system", "connection", "default", "devl"}
   280  			session := podmanTest.Podman(cmd)
   281  			session.WaitWithDefaultTimeout()
   282  			Expect(session).Should(ExitCleanly())
   283  			Expect(session.Out.Contents()).Should(BeEmpty())
   284  
   285  			Expect(config.ReadCustomConfig()).Should(HaveActiveService("devl"))
   286  
   287  			cmd = []string{"system", "connection", "list"}
   288  			session = podmanTest.Podman(cmd)
   289  			session.WaitWithDefaultTimeout()
   290  			Expect(session).Should(ExitCleanly())
   291  			Expect(session.Out).Should(Say("Name *URI *Identity *Default"))
   292  
   293  			cmd = []string{"system", "connection", "list", "--format", "{{.Name}}"}
   294  			session = podmanTest.Podman(cmd)
   295  			session.WaitWithDefaultTimeout()
   296  			Expect(session).Should(ExitCleanly())
   297  			Expect(session.OutputToString()).Should(Equal("devl qe"))
   298  		})
   299  
   300  		It("failed default", func() {
   301  			cmd := []string{"system", "connection", "default", "devl"}
   302  			session := podmanTest.Podman(cmd)
   303  			session.WaitWithDefaultTimeout()
   304  			Expect(session).ShouldNot(ExitCleanly())
   305  			Expect(session.Err).Should(Say("destination is not defined"))
   306  		})
   307  
   308  		It("failed rename", func() {
   309  			cmd := []string{"system", "connection", "rename", "devl", "QE"}
   310  			session := podmanTest.Podman(cmd)
   311  			session.WaitWithDefaultTimeout()
   312  			Expect(session).ShouldNot(ExitCleanly())
   313  			Expect(session.Err).Should(Say("destination is not defined"))
   314  		})
   315  
   316  		It("empty list", func() {
   317  			cmd := []string{"system", "connection", "list"}
   318  			session := podmanTest.Podman(cmd)
   319  			session.WaitWithDefaultTimeout()
   320  			Expect(session).Should(ExitCleanly())
   321  			Expect(session.OutputToStringArray()).Should(HaveLen(1))
   322  			Expect(session.Err.Contents()).Should(BeEmpty())
   323  		})
   324  	})
   325  
   326  	Context("sshd and API services required", func() {
   327  		BeforeEach(func() {
   328  			// These tests are unique in as much as they require podman, podman-remote, systemd and sshd.
   329  			// podman-remote commands will be executed by ginkgo directly.
   330  			SkipIfContainerized("sshd is not available when running in a container")
   331  			SkipIfRemote("connection heuristic requires both podman and podman-remote binaries")
   332  			SkipIfNotRootless(fmt.Sprintf("FIXME: set up ssh keys when root. uid(%d) euid(%d)", os.Getuid(), os.Geteuid()))
   333  			SkipIfSystemdNotRunning("cannot test connection heuristic if systemd is not running")
   334  			SkipIfNotActive("sshd", "cannot test connection heuristic if sshd is not running")
   335  		})
   336  
   337  		It("add ssh:// socket path using connection heuristic", func() {
   338  			u, err := user.Current()
   339  			Expect(err).ShouldNot(HaveOccurred())
   340  
   341  			// Ensure that the remote end uses our built podman
   342  			if os.Getenv("PODMAN_BINARY") == "" {
   343  				err = os.Setenv("PODMAN_BINARY", podmanTest.PodmanBinary)
   344  				Expect(err).ShouldNot(HaveOccurred())
   345  
   346  				defer func() {
   347  					os.Unsetenv("PODMAN_BINARY")
   348  				}()
   349  			}
   350  
   351  			cmd := exec.Command(podmanTest.RemotePodmanBinary,
   352  				"system", "connection", "add",
   353  				"--default",
   354  				"--identity", filepath.Join(u.HomeDir, ".ssh", "id_ed25519"),
   355  				"QA",
   356  				fmt.Sprintf("ssh://%s@localhost", u.Username))
   357  
   358  			session, err := Start(cmd, GinkgoWriter, GinkgoWriter)
   359  			Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("%q failed to execute", podmanTest.RemotePodmanBinary))
   360  			Eventually(session, DefaultWaitTimeout).Should(Exit(0))
   361  			Expect(session.Out.Contents()).Should(BeEmpty())
   362  			Expect(session.Err.Contents()).Should(BeEmpty())
   363  
   364  			cmd = exec.Command(podmanTest.RemotePodmanBinary,
   365  				"--connection", "QA", "ps")
   366  			_, err = Start(cmd, GinkgoWriter, GinkgoWriter)
   367  			Expect(err).ToNot(HaveOccurred())
   368  
   369  			// export the container_host env var and try again
   370  			err = os.Setenv("CONTAINER_HOST", fmt.Sprintf("ssh://%s@localhost", u.Username))
   371  			Expect(err).ToNot(HaveOccurred())
   372  			defer os.Unsetenv("CONTAINER_HOST")
   373  
   374  			cmd = exec.Command(podmanTest.RemotePodmanBinary, "ps")
   375  			_, err = Start(cmd, GinkgoWriter, GinkgoWriter)
   376  			Expect(err).ToNot(HaveOccurred())
   377  
   378  			uri := url.URL{
   379  				Scheme: "ssh",
   380  				User:   url.User(u.Username),
   381  				Host:   "localhost:22",
   382  				Path:   fmt.Sprintf("/run/user/%s/podman/podman.sock", u.Uid),
   383  			}
   384  
   385  			Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA"))
   386  			Expect(config.ReadCustomConfig()).Should(VerifyService(
   387  				"QA",
   388  				uri.String(),
   389  				filepath.Join(u.HomeDir, ".ssh", "id_ed25519"),
   390  			))
   391  		})
   392  	})
   393  })