Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit cde74be

Browse files
committed
Merge pull request #82 from feiskyer/update-exec
Fix hyper exec and attach
2 parents 8dde117 + 89e6838 commit cde74be

File tree

2 files changed

+129
-33
lines changed

2 files changed

+129
-33
lines changed

pkg/kubelet/hyper/hyper.go

+10-21
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,6 @@ func (r *runtime) buildHyperPod(pod *api.Pod, restartCount int, pullSecrets []ap
608608

609609
// other params required
610610
specMap[KEY_ID] = kubecontainer.BuildPodFullName(pod.Name, pod.Namespace)
611-
specMap[KEY_TTY] = false
612611

613612
// Cap hostname at 63 chars (specification is 64bytes which is 63 chars and the null terminating char).
614613
const hostnameMaxLen = 63
@@ -1152,7 +1151,7 @@ func (r *runtime) RunInContainer(containerID kubecontainer.ContainerID, cmd []st
11521151
glog.V(4).Infof("Hyper: running %s in container %s.", cmd, containerID.ID)
11531152

11541153
buffer := bytes.NewBuffer(nil)
1155-
err := r.ExecInContainer(containerID, cmd, nil, nopCloser{buffer}, nil, true)
1154+
err := r.ExecInContainer(containerID, cmd, nil, nopCloser{buffer}, nil, false)
11561155
if err != nil {
11571156
if exitErr, ok := err.(*exec.ExitError); ok {
11581157
err = &hyperExitError{exitErr}
@@ -1176,27 +1175,16 @@ func (r *runtime) PortForward(pod *kubecontainer.Pod, port uint16, stream io.Rea
11761175
func (r *runtime) ExecInContainer(containerID kubecontainer.ContainerID, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool) error {
11771176
glog.V(4).Infof("Hyper: execing %s in container %s.", cmd, containerID.ID)
11781177

1179-
args := append([]string{}, "exec", "-a", containerID.ID)
1180-
args = append(args, cmd...)
1181-
command := r.buildCommand(args...)
1182-
1183-
p, err := kubecontainer.StartPty(command)
1184-
if err != nil {
1185-
return err
1186-
}
1187-
defer p.Close()
1188-
1189-
// make sure to close the stdout stream
1190-
defer stdout.Close()
1191-
1192-
if stdin != nil {
1193-
go io.Copy(p, stdin)
1178+
opts := ExecInContainerOptions{
1179+
Container: containerID.ID,
1180+
InputStream: stdin,
1181+
OutputStream: stdout,
1182+
ErrorStream: stderr,
1183+
Commands: cmd,
1184+
TTY: tty,
11941185
}
11951186

1196-
if stdout != nil {
1197-
go io.Copy(stdout, p)
1198-
}
1199-
return command.Wait()
1187+
return r.hyperClient.Exec(opts)
12001188
}
12011189

12021190
func (r *runtime) AttachContainer(containerID kubecontainer.ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool) error {
@@ -1207,6 +1195,7 @@ func (r *runtime) AttachContainer(containerID kubecontainer.ContainerID, stdin i
12071195
InputStream: stdin,
12081196
OutputStream: stdout,
12091197
ErrorStream: stderr,
1198+
TTY: tty,
12101199
}
12111200

12121201
return r.hyperClient.Attach(opts)

pkg/kubelet/hyper/hyperclient.go

+119-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package hyper
1818

1919
import (
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

99102
type 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+
110122
type hijackOptions struct {
111123
in io.Reader
112124
stdout io.Writer
113125
stderr io.Writer
114126
data interface{}
127+
tty bool
115128
}
116129

117130
func 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+
530577
func (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

603671
func (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

619726
func (client *HyperClient) ContainerLogs(opts ContainerLogsOptions) error {

0 commit comments

Comments
 (0)