mirror of https://github.com/docker/cli.git
cli/streams: minor refactoring and docs touch-ups
- Touch-up GoDoc to better document each method, adding punctuation, and
use doc-links where applicable.
- SetRawTerminal(): change the order in which we check if a terminal is
connected; check the local boolean first before checking if the NORAW
env-var is set.
- NewOut() / NewIn(); remove intermediate variables
- Remove explicit use of the embedded "commonStream" to make the code
slightly less verbose, and more "to the point".
- Document the intended purpose of SetIsTerminal(), which was added in
b2551c619d
to be used in unit-tests.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
777a2edc57
commit
8080326526
|
@ -9,38 +9,42 @@ import (
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
// In is an input stream used by the DockerCli to read user input
|
// In is an input stream to read user input. It implements [io.ReadCloser]
|
||||||
|
// with additional utilities, such as putting the terminal in raw mode.
|
||||||
type In struct {
|
type In struct {
|
||||||
commonStream
|
commonStream
|
||||||
in io.ReadCloser
|
in io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read implements the [io.Reader] interface.
|
||||||
func (i *In) Read(p []byte) (int, error) {
|
func (i *In) Read(p []byte) (int, error) {
|
||||||
return i.in.Read(p)
|
return i.in.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements the Closer interface
|
// Close implements the [io.Closer] interface.
|
||||||
func (i *In) Close() error {
|
func (i *In) Close() error {
|
||||||
return i.in.Close()
|
return i.in.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRawTerminal sets raw mode on the input terminal
|
// SetRawTerminal sets raw mode on the input terminal. It is a no-op if In
|
||||||
|
// is not a TTY, or if the "NORAW" environment variable is set to a non-empty
|
||||||
|
// value.
|
||||||
func (i *In) SetRawTerminal() (err error) {
|
func (i *In) SetRawTerminal() (err error) {
|
||||||
if os.Getenv("NORAW") != "" || !i.commonStream.isTerminal {
|
if !i.isTerminal || os.Getenv("NORAW") != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
i.commonStream.state, err = term.SetRawTerminal(i.commonStream.fd)
|
i.state, err = term.SetRawTerminal(i.fd)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTty checks if we are trying to attach to a container tty
|
// CheckTty checks if we are trying to attach to a container TTY
|
||||||
// from a non-tty client input stream, and if so, returns an error.
|
// from a non-TTY client input stream, and if so, returns an error.
|
||||||
func (i *In) CheckTty(attachStdin, ttyMode bool) error {
|
func (i *In) CheckTty(attachStdin, ttyMode bool) error {
|
||||||
// In order to attach to a container tty, input stream for the client must
|
// In order to attach to a container tty, input stream for the client must
|
||||||
// be a tty itself: redirecting or piping the client standard input is
|
// be a tty itself: redirecting or piping the client standard input is
|
||||||
// incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
|
// incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
|
||||||
if ttyMode && attachStdin && !i.isTerminal {
|
if ttyMode && attachStdin && !i.isTerminal {
|
||||||
eText := "the input device is not a TTY"
|
const eText = "the input device is not a TTY"
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
return errors.New(eText + ". If you are using mintty, try prefixing the command with 'winpty'")
|
return errors.New(eText + ". If you are using mintty, try prefixing the command with 'winpty'")
|
||||||
}
|
}
|
||||||
|
@ -49,8 +53,9 @@ func (i *In) CheckTty(attachStdin, ttyMode bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIn returns a new In object from a ReadCloser
|
// NewIn returns a new [In] from an [io.ReadCloser].
|
||||||
func NewIn(in io.ReadCloser) *In {
|
func NewIn(in io.ReadCloser) *In {
|
||||||
fd, isTerminal := term.GetFdInfo(in)
|
i := &In{in: in}
|
||||||
return &In{commonStream: commonStream{fd: fd, isTerminal: isTerminal}, in: in}
|
i.fd, i.isTerminal = term.GetFdInfo(in)
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,9 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Out is an output stream used by the DockerCli to write normal program
|
// Out is an output stream to write normal program output. It implements
|
||||||
// output.
|
// an [io.Writer], with additional utilities for detecting whether a terminal
|
||||||
|
// is connected, getting the TTY size, and putting the terminal in raw mode.
|
||||||
type Out struct {
|
type Out struct {
|
||||||
commonStream
|
commonStream
|
||||||
out io.Writer
|
out io.Writer
|
||||||
|
@ -19,23 +20,29 @@ func (o *Out) Write(p []byte) (int, error) {
|
||||||
return o.out.Write(p)
|
return o.out.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRawTerminal sets raw mode on the input terminal
|
// SetRawTerminal puts the output of the terminal connected to the stream
|
||||||
|
// into raw mode.
|
||||||
|
//
|
||||||
|
// On UNIX, this does nothing. On Windows, it disables LF -> CRLF/ translation.
|
||||||
|
// It is a no-op if Out is not a TTY, or if the "NORAW" environment variable is
|
||||||
|
// set to a non-empty value.
|
||||||
func (o *Out) SetRawTerminal() (err error) {
|
func (o *Out) SetRawTerminal() (err error) {
|
||||||
if os.Getenv("NORAW") != "" || !o.commonStream.isTerminal {
|
if !o.isTerminal || os.Getenv("NORAW") != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
o.commonStream.state, err = term.SetRawTerminalOutput(o.commonStream.fd)
|
o.state, err = term.SetRawTerminalOutput(o.fd)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTtySize returns the height and width in characters of the tty
|
// GetTtySize returns the height and width in characters of the TTY, or
|
||||||
func (o *Out) GetTtySize() (uint, uint) {
|
// zero for both if no TTY is connected.
|
||||||
|
func (o *Out) GetTtySize() (height uint, width uint) {
|
||||||
if !o.isTerminal {
|
if !o.isTerminal {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
ws, err := term.GetWinsize(o.fd)
|
ws, err := term.GetWinsize(o.fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error getting size: %s", err)
|
logrus.WithError(err).Debug("Error getting TTY size")
|
||||||
if ws == nil {
|
if ws == nil {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
@ -43,8 +50,9 @@ func (o *Out) GetTtySize() (uint, uint) {
|
||||||
return uint(ws.Height), uint(ws.Width)
|
return uint(ws.Height), uint(ws.Width)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOut returns a new Out object from a Writer
|
// NewOut returns a new [Out] from an [io.Writer].
|
||||||
func NewOut(out io.Writer) *Out {
|
func NewOut(out io.Writer) *Out {
|
||||||
fd, isTerminal := term.GetFdInfo(out)
|
o := &Out{out: out}
|
||||||
return &Out{commonStream: commonStream{fd: fd, isTerminal: isTerminal}, out: out}
|
o.fd, o.isTerminal = term.GetFdInfo(out)
|
||||||
|
return o
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,31 +4,32 @@ import (
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
// commonStream is an input stream used by the DockerCli to read user input
|
|
||||||
type commonStream struct {
|
type commonStream struct {
|
||||||
fd uintptr
|
fd uintptr
|
||||||
isTerminal bool
|
isTerminal bool
|
||||||
state *term.State
|
state *term.State
|
||||||
}
|
}
|
||||||
|
|
||||||
// FD returns the file descriptor number for this stream
|
// FD returns the file descriptor number for this stream.
|
||||||
func (s *commonStream) FD() uintptr {
|
func (s *commonStream) FD() uintptr {
|
||||||
return s.fd
|
return s.fd
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsTerminal returns true if this stream is connected to a terminal
|
// IsTerminal returns true if this stream is connected to a terminal.
|
||||||
func (s *commonStream) IsTerminal() bool {
|
func (s *commonStream) IsTerminal() bool {
|
||||||
return s.isTerminal
|
return s.isTerminal
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestoreTerminal restores normal mode to the terminal
|
// RestoreTerminal restores normal mode to the terminal.
|
||||||
func (s *commonStream) RestoreTerminal() {
|
func (s *commonStream) RestoreTerminal() {
|
||||||
if s.state != nil {
|
if s.state != nil {
|
||||||
term.RestoreTerminal(s.fd, s.state)
|
_ = term.RestoreTerminal(s.fd, s.state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetIsTerminal sets the boolean used for isTerminal
|
// SetIsTerminal overrides whether a terminal is connected. It is used to
|
||||||
|
// override this property in unit-tests, and should not be depended on for
|
||||||
|
// other purposes.
|
||||||
func (s *commonStream) SetIsTerminal(isTerminal bool) {
|
func (s *commonStream) SetIsTerminal(isTerminal bool) {
|
||||||
s.isTerminal = isTerminal
|
s.isTerminal = isTerminal
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue