Fix terminal attach

Re-order the startup of a new container via run from
initialize > start > attach to initialize > attach > start.

This fixes output when running:

kpod run -i -t IMAGE command

and

kpod run IMAGE command

Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
baude 2017-11-08 15:14:33 -06:00
parent 5cfd7a313f
commit acd9c66864
6 changed files with 53 additions and 18 deletions

View file

@ -10,6 +10,8 @@ host:
required: true required: true
timeout: 45m
tests: tests:
# mount yum repos to inherit injected mirrors from PAPR # mount yum repos to inherit injected mirrors from PAPR
- docker run --net=host --privileged -v /etc/yum.repos.d:/etc/yum.repos.d.host:ro - docker run --net=host --privileged -v /etc/yum.repos.d:/etc/yum.repos.d.host:ro

View file

@ -112,7 +112,7 @@ testunit:
$(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES) $(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES)
localintegration: test-binaries localintegration: test-binaries
./test/test_runner.sh ${TESTFLAGS} bash -i ./test/test_runner.sh ${TESTFLAGS}
binaries: conmon kpod binaries: conmon kpod

View file

@ -319,6 +319,14 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
blkioWeight = uint16(u) blkioWeight = uint16(u)
} }
// Because we cannot do a non-terminal attach, we need to set tty to true
// if detach is not false
// TODO Allow non-terminal attach
tty := c.Bool("tty")
if !c.Bool("detach") && !tty {
tty = true
}
config := &createConfig{ config := &createConfig{
capAdd: c.StringSlice("cap-add"), capAdd: c.StringSlice("cap-add"),
capDrop: c.StringSlice("cap-drop"), capDrop: c.StringSlice("cap-drop"),
@ -387,7 +395,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
storageOpts: c.StringSlice("storage-opt"), storageOpts: c.StringSlice("storage-opt"),
sysctl: sysctl, sysctl: sysctl,
tmpfs: c.StringSlice("tmpfs"), tmpfs: c.StringSlice("tmpfs"),
tty: c.Bool("tty"), tty: tty,
user: uid, user: uid,
group: gid, group: gid,
volumes: c.StringSlice("volume"), volumes: c.StringSlice("volume"),

View file

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"sync"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/projectatomic/libpod/libpod" "github.com/projectatomic/libpod/libpod"
@ -91,20 +92,38 @@ func runCmd(c *cli.Context) error {
libpod.WriteFile(ctr.ID(), c.String("cidfile")) libpod.WriteFile(ctr.ID(), c.String("cidfile"))
return nil return nil
} }
// Create a bool channel to track that the console socket attach
// is successful.
attached := make(chan bool)
// Create a waitgroup so we can sync and wait for all goroutines
// to finish before exiting main
var wg sync.WaitGroup
if !createConfig.detach {
// We increment the wg counter because we need to do the attach
wg.Add(1)
// Attach to the running container
go func() {
logrus.Debug("trying to attach to the container %s", ctr.ID())
defer wg.Done()
if err := ctr.Attach(false, c.String("detach-keys"), attached); err != nil {
logrus.Errorf("unable to attach to container %s: %q", ctr.ID(), err)
}
}()
if !<-attached {
return errors.Errorf("unable to attach to container %s", ctr.ID())
}
}
// Start the container // Start the container
if err := ctr.Start(); err != nil { if err := ctr.Start(); err != nil {
return errors.Wrapf(err, "unable to start container %q", ctr.ID()) return errors.Wrapf(err, "unable to start container %q", ctr.ID())
} }
logrus.Debug("started container ", ctr.ID()) logrus.Debug("started container ", ctr.ID())
if createConfig.tty {
// Attach to the running container if createConfig.detach {
logrus.Debug("trying to attach to the container %s", ctr.ID())
if err := ctr.Attach(false, c.String("detach-keys")); err != nil {
return errors.Wrapf(err, "unable to attach to container %s", ctr.ID())
}
} else {
fmt.Printf("%s\n", ctr.ID()) fmt.Printf("%s\n", ctr.ID())
} }
wg.Wait()
return nil return nil
} }

View file

@ -393,7 +393,7 @@ func (c *Container) Exec(cmd []string, tty bool, stdin bool) (string, error) {
// Attach attaches to a container // Attach attaches to a container
// Returns fully qualified URL of streaming server for the container // Returns fully qualified URL of streaming server for the container
func (c *Container) Attach(noStdin bool, keys string) error { func (c *Container) Attach(noStdin bool, keys string, attached chan<- bool) error {
// Check the validity of the provided keys first // Check the validity of the provided keys first
var err error var err error
detachKeys := []byte{} detachKeys := []byte{}
@ -410,7 +410,7 @@ func (c *Container) Attach(noStdin bool, keys string) error {
} }
resize := make(chan remotecommand.TerminalSize) resize := make(chan remotecommand.TerminalSize)
defer close(resize) defer close(resize)
err = c.attachContainerSocket(resize, noStdin, detachKeys) err = c.attachContainerSocket(resize, noStdin, detachKeys, attached)
if err != nil { if err != nil {
return err return err
} }

View file

@ -2,6 +2,7 @@ package libpod
import ( import (
"fmt" "fmt"
"golang.org/x/crypto/ssh/terminal"
"io" "io"
"net" "net"
"os" "os"
@ -25,7 +26,7 @@ const (
) )
// attachContainerSocket connects to the container's attach socket and deals with the IO // attachContainerSocket connects to the container's attach socket and deals with the IO
func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSize, noStdIn bool, detachKeys []byte) error { func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSize, noStdIn bool, detachKeys []byte, attached chan<- bool) error {
inputStream := os.Stdin inputStream := os.Stdin
outputStream := os.Stdout outputStream := os.Stdout
errorStream := os.Stderr errorStream := os.Stderr
@ -38,12 +39,14 @@ func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSi
return errors.Errorf("no tty available for %s", c.ID()) return errors.Errorf("no tty available for %s", c.ID())
} }
oldTermState, err := term.SaveState(inputStream.Fd()) if terminal.IsTerminal(int(inputStream.Fd())) {
if err != nil { oldTermState, err := term.SaveState(inputStream.Fd())
return errors.Wrapf(err, "unable to save terminal state") if err != nil {
} return errors.Wrapf(err, "unable to save terminal state")
}
defer term.RestoreTerminal(inputStream.Fd(), oldTermState) defer term.RestoreTerminal(inputStream.Fd(), oldTermState)
}
// Put both input and output into raw // Put both input and output into raw
if !noStdIn { if !noStdIn {
@ -71,6 +74,9 @@ func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSi
} }
defer conn.Close() defer conn.Close()
// signal back that the connection was made
attached <- true
receiveStdoutError := make(chan error) receiveStdoutError := make(chan error)
if outputStream != nil || errorStream != nil { if outputStream != nil || errorStream != nil {
go func() { go func() {