-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11819 from trynaeat/dial-stdio
Adding dial-stdio CLI cmd
- Loading branch information
Showing
3 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package system | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"os" | ||
|
||
"github.com/containers/podman/v3/cmd/podman/registry" | ||
"github.com/containers/podman/v3/cmd/podman/validate" | ||
"github.com/containers/podman/v3/pkg/bindings" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
dialStdioCommand = &cobra.Command{ | ||
Use: "dial-stdio", | ||
Short: "Proxy the stdio stream to the daemon connection. Should not be invoked manually.", | ||
Args: validate.NoArgs, | ||
Hidden: true, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return runDialStdio() | ||
}, | ||
Example: "podman system dial-stdio", | ||
} | ||
) | ||
|
||
func init() { | ||
registry.Commands = append(registry.Commands, registry.CliCommand{ | ||
Command: dialStdioCommand, | ||
Parent: systemCmd, | ||
}) | ||
} | ||
|
||
func runDialStdio() error { | ||
ctx := registry.Context() | ||
cfg := registry.PodmanConfig() | ||
ctx, cancel := context.WithCancel(ctx) | ||
defer cancel() | ||
bindCtx, err := bindings.NewConnection(ctx, cfg.URI) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to open connection to podman") | ||
} | ||
conn, err := bindings.GetClient(bindCtx) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to get connection after initialization") | ||
} | ||
netConn, err := conn.GetDialer(bindCtx) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to open the raw stream connection") | ||
} | ||
defer netConn.Close() | ||
|
||
var connHalfCloser halfCloser | ||
switch t := netConn.(type) { | ||
case halfCloser: | ||
connHalfCloser = t | ||
case halfReadWriteCloser: | ||
connHalfCloser = &nopCloseReader{t} | ||
default: | ||
return errors.New("the raw stream connection does not implement halfCloser") | ||
} | ||
|
||
stdin2conn := make(chan error, 1) | ||
conn2stdout := make(chan error, 1) | ||
go func() { | ||
stdin2conn <- copier(connHalfCloser, &halfReadCloserWrapper{os.Stdin}, "stdin to stream") | ||
}() | ||
go func() { | ||
conn2stdout <- copier(&halfWriteCloserWrapper{os.Stdout}, connHalfCloser, "stream to stdout") | ||
}() | ||
select { | ||
case err = <-stdin2conn: | ||
if err != nil { | ||
return err | ||
} | ||
// wait for stdout | ||
err = <-conn2stdout | ||
case err = <-conn2stdout: | ||
// return immediately | ||
} | ||
return err | ||
} | ||
|
||
// Below portion taken from original docker CLI | ||
// https://github.com/docker/cli/blob/v20.10.9/cli/command/system/dial_stdio.go | ||
func copier(to halfWriteCloser, from halfReadCloser, debugDescription string) error { | ||
defer func() { | ||
if err := from.CloseRead(); err != nil { | ||
logrus.Errorf("error while CloseRead (%s): %v", debugDescription, err) | ||
} | ||
if err := to.CloseWrite(); err != nil { | ||
logrus.Errorf("error while CloseWrite (%s): %v", debugDescription, err) | ||
} | ||
}() | ||
if _, err := io.Copy(to, from); err != nil { | ||
return errors.Wrapf(err, "error while Copy (%s)", debugDescription) | ||
} | ||
return nil | ||
} | ||
|
||
type halfReadCloser interface { | ||
io.Reader | ||
CloseRead() error | ||
} | ||
|
||
type halfWriteCloser interface { | ||
io.Writer | ||
CloseWrite() error | ||
} | ||
|
||
type halfCloser interface { | ||
halfReadCloser | ||
halfWriteCloser | ||
} | ||
|
||
type halfReadWriteCloser interface { | ||
io.Reader | ||
halfWriteCloser | ||
} | ||
|
||
type nopCloseReader struct { | ||
halfReadWriteCloser | ||
} | ||
|
||
func (x *nopCloseReader) CloseRead() error { | ||
return nil | ||
} | ||
|
||
type halfReadCloserWrapper struct { | ||
io.ReadCloser | ||
} | ||
|
||
func (x *halfReadCloserWrapper) CloseRead() error { | ||
return x.Close() | ||
} | ||
|
||
type halfWriteCloserWrapper struct { | ||
io.WriteCloser | ||
} | ||
|
||
func (x *halfWriteCloserWrapper) CloseWrite() error { | ||
return x.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package integration | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
. "github.com/containers/podman/v3/test/utils" | ||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
. "github.com/onsi/gomega/gexec" | ||
) | ||
|
||
var _ = Describe("podman system dial-stdio", func() { | ||
var ( | ||
tempdir string | ||
err error | ||
podmanTest *PodmanTestIntegration | ||
) | ||
|
||
BeforeEach(func() { | ||
tempdir, err = CreateTempDirInTempDir() | ||
if err != nil { | ||
os.Exit(1) | ||
} | ||
podmanTest = PodmanTestCreate(tempdir) | ||
podmanTest.Setup() | ||
podmanTest.SeedImages() | ||
}) | ||
|
||
AfterEach(func() { | ||
podmanTest.Cleanup() | ||
f := CurrentGinkgoTestDescription() | ||
timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) | ||
GinkgoWriter.Write([]byte(timedResult)) | ||
}) | ||
|
||
It("podman system dial-stdio help", func() { | ||
session := podmanTest.Podman([]string{"system", "dial-stdio", "--help"}) | ||
session.WaitWithDefaultTimeout() | ||
Expect(session).Should(Exit(0)) | ||
Expect(session.OutputToString()).To(ContainSubstring("Examples: podman system dial-stdio")) | ||
}) | ||
|
||
It("podman system dial-stdio while service is not running", func() { | ||
if IsRemote() { | ||
Skip("this test is only for non-remote") | ||
} | ||
session := podmanTest.Podman([]string{"system", "dial-stdio"}) | ||
session.WaitWithDefaultTimeout() | ||
Expect(session).Should(Exit(125)) | ||
Expect(session.ErrorToString()).To(ContainSubstring("Error: failed to open connection to podman")) | ||
}) | ||
}) |