@@ -18,6 +18,7 @@ package hyper
1818
1919import (
2020 "bytes"
21+ "crypto/rand"
2122 "encoding/json"
2223 "errors"
2324 "fmt"
@@ -31,6 +32,7 @@ import (
3132 "strings"
3233
3334 "github.com/docker/docker/pkg/parsers"
35+ "github.com/docker/docker/pkg/term"
3436 "github.com/golang/glog"
3537 "time"
3638)
@@ -94,6 +96,7 @@ type AttachToContainerOptions struct {
9496 InputStream io.Reader
9597 OutputStream io.Writer
9698 ErrorStream io.Writer
99+ TTY bool
97100}
98101
99102type ContainerLogsOptions struct {
@@ -107,11 +110,21 @@ type ContainerLogsOptions struct {
107110 TailLines int64
108111}
109112
113+ type ExecInContainerOptions struct {
114+ Container string
115+ InputStream io.Reader
116+ OutputStream io.Writer
117+ ErrorStream io.Writer
118+ Commands []string
119+ TTY bool
120+ }
121+
110122type hijackOptions struct {
111123 in io.Reader
112124 stdout io.Writer
113125 stderr io.Writer
114126 data interface {}
127+ tty bool
115128}
116129
117130func NewHyperClient () * HyperClient {
@@ -527,6 +540,40 @@ func (client *HyperClient) CreatePod(podArgs string) (map[string]interface{}, er
527540 return result , nil
528541}
529542
543+ func (c * HyperClient ) GetExitCode (container , tag string ) error {
544+ v := url.Values {}
545+ v .Set ("container" , container )
546+ v .Set ("tag" , tag )
547+ code := - 1
548+
549+ body , _ , err := c .call ("GET" , "/exitcode?" + v .Encode (), "" , nil )
550+ if err != nil {
551+ return err
552+ }
553+
554+ err = json .Unmarshal (body , & code )
555+ if err != nil {
556+ return err
557+ }
558+
559+ if code != 0 {
560+ return fmt .Errorf ("Exit code %d" , code )
561+ }
562+
563+ return nil
564+ }
565+
566+ func (c * HyperClient ) GetTag () string {
567+ dictionary := "0123456789abcdefghijklmnopqrstuvwxyz"
568+
569+ var bytes = make ([]byte , 8 )
570+ rand .Read (bytes )
571+ for k , v := range bytes {
572+ bytes [k ] = dictionary [v % byte (len (dictionary ))]
573+ }
574+ return string (bytes )
575+ }
576+
530577func (c * HyperClient ) hijack (method , path string , hijackOptions hijackOptions ) error {
531578 var params io.Reader
532579 if hijackOptions .data != nil {
@@ -537,12 +584,18 @@ func (c *HyperClient) hijack(method, path string, hijackOptions hijackOptions) e
537584 params = bytes .NewBuffer (buf )
538585 }
539586
540- if hijackOptions .stdout == nil {
541- hijackOptions .stdout = ioutil .Discard
542- }
543- if hijackOptions .stderr == nil {
544- hijackOptions .stderr = ioutil .Discard
587+ if hijackOptions .tty {
588+ in , isTerm := term .GetFdInfo (hijackOptions .in )
589+ if isTerm {
590+ state , err := term .SetRawTerminal (in )
591+ if err != nil {
592+ return err
593+ }
594+
595+ defer term .RestoreTerminal (in , state )
596+ }
545597 }
598+
546599 req , err := http .NewRequest (method , fmt .Sprintf ("/v%s%s" , HYPER_MINVERSION , path ), params )
547600 if err != nil {
548601 return err
@@ -571,9 +624,22 @@ func (c *HyperClient) hijack(method, path string, hijackOptions hijackOptions) e
571624 errChanIn := make (chan error , 1 )
572625 exit := make (chan bool )
573626
627+ if hijackOptions .stdout == nil && hijackOptions .stderr == nil {
628+ close (errChanOut )
629+ }
630+ if hijackOptions .stdout == nil {
631+ hijackOptions .stdout = ioutil .Discard
632+ }
633+ if hijackOptions .stderr == nil {
634+ hijackOptions .stderr = ioutil .Discard
635+ }
636+
574637 go func () {
575- defer close (exit )
576- defer close (errChanOut )
638+ defer func () {
639+ close (errChanOut )
640+ close (exit )
641+ }()
642+
577643 _ , err := io .Copy (hijackOptions .stdout , br )
578644 errChanOut <- err
579645 }()
@@ -584,11 +650,11 @@ func (c *HyperClient) hijack(method, path string, hijackOptions hijackOptions) e
584650 if hijackOptions .in != nil {
585651 _ , err := io .Copy (rwc , hijackOptions .in )
586652 errChanIn <- err
587- }
588653
589- rwc .(interface {
590- CloseWrite () error
591- }).CloseWrite ()
654+ rwc .(interface {
655+ CloseWrite () error
656+ }).CloseWrite ()
657+ }
592658 }()
593659
594660 <- exit
@@ -598,22 +664,63 @@ func (c *HyperClient) hijack(method, path string, hijackOptions hijackOptions) e
598664 case err = <- errChanOut :
599665 return err
600666 }
667+
668+ return nil
601669}
602670
603671func (client * HyperClient ) Attach (opts AttachToContainerOptions ) error {
604672 if opts .Container == "" {
605673 return fmt .Errorf ("No Such Container %s" , opts .Container )
606674 }
607675
676+ tag := client .GetTag ()
608677 v := url.Values {}
609678 v .Set (KEY_TYPE , TYPE_CONTAINER )
610679 v .Set (KEY_VALUE , opts .Container )
680+ v .Set ("tag" , tag )
611681 path := "/attach?" + v .Encode ()
612- return client .hijack ("POST" , path , hijackOptions {
682+ err := client .hijack ("POST" , path , hijackOptions {
683+ in : opts .InputStream ,
684+ stdout : opts .OutputStream ,
685+ stderr : opts .ErrorStream ,
686+ tty : opts .TTY ,
687+ })
688+
689+ if err != nil {
690+ return err
691+ }
692+
693+ return client .GetExitCode (opts .Container , tag )
694+ }
695+
696+ func (client * HyperClient ) Exec (opts ExecInContainerOptions ) error {
697+ if opts .Container == "" {
698+ return fmt .Errorf ("No Such Container %s" , opts .Container )
699+ }
700+
701+ command , err := json .Marshal (opts .Commands )
702+ if err != nil {
703+ return err
704+ }
705+
706+ v := url.Values {}
707+ tag := client .GetTag ()
708+ v .Set (KEY_TYPE , TYPE_CONTAINER )
709+ v .Set (KEY_VALUE , opts .Container )
710+ v .Set ("tag" , tag )
711+ v .Set ("command" , string (command ))
712+ path := "/exec?" + v .Encode ()
713+ err = client .hijack ("POST" , path , hijackOptions {
613714 in : opts .InputStream ,
614715 stdout : opts .OutputStream ,
615716 stderr : opts .ErrorStream ,
717+ tty : opts .TTY ,
616718 })
719+ if err != nil {
720+ return err
721+ }
722+
723+ return client .GetExitCode (opts .Container , tag )
617724}
618725
619726func (client * HyperClient ) ContainerLogs (opts ContainerLogsOptions ) error {
0 commit comments