Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 57 additions & 20 deletions cmd/podman/system/connection/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,24 @@ import (
"golang.org/x/crypto/ssh/agent"
)

const schemaPattern = "^[A-Za-z][A-Za-z0-9+.-]*:"

var (
addCmd = &cobra.Command{
Use: "add [options] NAME DESTINATION",
Args: cobra.ExactArgs(2),
Short: "Record destination for the Podman service",
Long: `Add destination to podman configuration.
"destination" is of the form [user@]hostname or
an URI of the form ssh://[user@]hostname[:port]
"destination" is one of the form:
[user@]hostname (will default to ssh)
ssh://[user@]hostname[:port][/path] (will obtain socket path from service, if not given.)
tcp://hostname:port (not secured)
unix://path (absolute path required)
`,
RunE: add,
ValidArgsFunction: completion.AutocompleteNone,
Example: `podman system connection add laptop server.fubar.com
podman system connection add --identity ~/.ssh/dev_rsa testing ssh://root@server.fubar.com:2222
podman system connection add --identity ~/.ssh/dev_rsa --port 22 production root@server.fubar.com
podman system connection add debug tcp://localhost:8080
`,
}

Expand Down Expand Up @@ -74,9 +76,9 @@ func init() {
}

func add(cmd *cobra.Command, args []string) error {
// Default to ssh: schema if none given
// Default to ssh schema if none given
dest := args[1]
if match, err := regexp.Match(schemaPattern, []byte(dest)); err != nil {
if match, err := regexp.Match("^[A-Za-z][A-Za-z0-9+.-]*://", []byte(dest)); err != nil {
return errors.Wrapf(err, "invalid destination")
} else if !match {
dest = "ssh://" + dest
Expand All @@ -87,28 +89,63 @@ func add(cmd *cobra.Command, args []string) error {
return err
}

if uri.User.Username() == "" {
if uri.User, err = getUserInfo(uri); err != nil {
return err
}
}

if cmd.Flags().Changed("socket-path") {
uri.Path = cmd.Flag("socket-path").Value.String()
}

if cmd.Flags().Changed("port") {
uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").Value.String())
}
switch uri.Scheme {
case "ssh":
if uri.User.Username() == "" {
if uri.User, err = getUserInfo(uri); err != nil {
return err
}
}

if uri.Port() == "" {
uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").DefValue)
}
if cmd.Flags().Changed("port") {
uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").Value.String())
}

if uri.Path == "" || uri.Path == "/" {
if uri.Path, err = getUDS(cmd, uri); err != nil {
if uri.Port() == "" {
uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").DefValue)
}

if uri.Path == "" || uri.Path == "/" {
if uri.Path, err = getUDS(cmd, uri); err != nil {
return err
}
}
case "unix":
if cmd.Flags().Changed("identity") {
return errors.New("--identity option not supported for unix scheme")
}

if cmd.Flags().Changed("socket-path") {
uri.Path = cmd.Flag("socket-path").Value.String()
}

info, err := os.Stat(uri.Path)
switch {
case errors.Is(err, os.ErrNotExist):
logrus.Warnf("%q does not exists", uri.Path)
case errors.Is(err, os.ErrPermission):
logrus.Warnf("You do not have permission to read %q", uri.Path)
case err != nil:
return err
case info.Mode()&os.ModeSocket == 0:
return fmt.Errorf("%q exists and is not a unix domain socket", uri.Path)
}
case "tcp":
if cmd.Flags().Changed("socket-path") {
return errors.New("--socket-path option not supported for tcp scheme")
}
if cmd.Flags().Changed("identity") {
return errors.New("--identity option not supported for tcp scheme")
}
if uri.Port() == "" {
return errors.New("tcp scheme requires a port either via --port or in destination URL")
}
default:
logrus.Warnf("%q unknown scheme, no validation provided", uri.Scheme)
}

cfg, err := config.ReadCustomConfig()
Expand Down
6 changes: 6 additions & 0 deletions docs/source/markdown/podman-system-connection-add.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ podman\-system\-connection\-add - Record destination for the Podman service
Record ssh destination for remote podman service(s). The ssh destination is given as one of:
- [user@]hostname[:port]
- ssh://[user@]hostname[:port]
- unix://path
- tcp://hostname:port

The user will be prompted for the remote ssh login password or key file pass phrase as required. The `ssh-agent` is supported if it is running.

Expand Down Expand Up @@ -38,6 +40,10 @@ Path to the Podman service unix domain socket on the ssh destination host
$ podman system connection add QA podman.example.com

$ podman system connection add --identity ~/.ssh/dev_rsa production ssh://root@server.example.com:2222

$ podman system connection add unix:///run/podman/podman.sock

$ podman system connection add tcp://localhost:8080
```
## SEE ALSO
podman-system(1) , podman-system-connection(1) , containers.conf(5)
Expand Down
64 changes: 63 additions & 1 deletion test/e2e/system_connection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ var _ = Describe("podman system connection", func() {
GinkgoWriter.Write([]byte(timedResult))
})

It("add", func() {
It("add ssh://", func() {
cmd := []string{"system", "connection", "add",
"--default",
"--identity", "~/.ssh/id_rsa",
Expand Down Expand Up @@ -94,6 +94,68 @@ var _ = Describe("podman system connection", func() {
))
})

It("add UDS", func() {
cmd := []string{"system", "connection", "add",
"QA-UDS",
"unix:///run/podman/podman.sock",
}
session := podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.Out).Should(Say(""))

cfg, err := config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Engine.ActiveService).To(Equal("QA-UDS"))
Expect(cfg.Engine.ServiceDestinations["QA-UDS"]).To(Equal(
config.Destination{
URI: "unix:///run/podman/podman.sock",
Identity: "",
},
))

cmd = []string{"system", "connection", "add",
"QA-UDS1",
"--socket-path", "/run/user/podman/podman.sock",
"unix:///run/podman/podman.sock",
}
session = podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.Out).Should(Say(""))

cfg, err = config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Engine.ActiveService).To(Equal("QA-UDS"))
Expect(cfg.Engine.ServiceDestinations["QA-UDS1"]).To(Equal(
config.Destination{
URI: "unix:///run/user/podman/podman.sock",
Identity: "",
},
))
})

It("add tcp", func() {
cmd := []string{"system", "connection", "add",
"QA-TCP",
"tcp://localhost:8080",
}
session := podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.Out).Should(Say(""))

cfg, err := config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Engine.ActiveService).To(Equal("QA-TCP"))
Expect(cfg.Engine.ServiceDestinations["QA-TCP"]).To(Equal(
config.Destination{
URI: "tcp://localhost:8080",
Identity: "",
},
))
})

It("remove", func() {
cmd := []string{"system", "connection", "add",
"--default",
Expand Down