/
kube-sshd.go
124 lines (101 loc) · 2.55 KB
/
kube-sshd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package kubesshd
import (
"context"
"fmt"
"github.com/tg123/docker-sshd/pkg/bridge"
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/scheme"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/client-go/util/exec"
)
var _ bridge.SessionProvider = (*kubesshdconn)(nil)
type kubesshdconn struct {
config *restclient.Config
namespace string
pod string
container string
cancel context.CancelFunc
resizeQueue chan *remotecommand.TerminalSize
}
func (k *kubesshdconn) Close() error {
if k.cancel != nil {
k.cancel()
}
return nil
}
func (k *kubesshdconn) Next() *remotecommand.TerminalSize {
size, ok := <-k.resizeQueue
if !ok {
return nil
}
return size
}
func (k *kubesshdconn) Exec(ctx context.Context, execconfig bridge.ExecConfig) (<-chan bridge.ExecResult, error) {
corev1client, err := corev1.NewForConfig(k.config)
if err != nil {
return nil, err
}
req := corev1client.RESTClient().Post().
Resource("pods").
Name(k.pod).
Namespace(k.namespace).
SubResource("exec")
executor, err := remotecommand.NewSPDYExecutor(k.config, "POST", req.VersionedParams(
&v1.PodExecOptions{
Container: k.container,
Command: execconfig.Cmd,
Stdin: true,
Stdout: true,
Stderr: true,
TTY: execconfig.Tty,
},
scheme.ParameterCodec,
).URL())
if err != nil {
return nil, err
}
r := make(chan bridge.ExecResult)
go func() {
defer k.Close()
ctx, cancel := context.WithCancel(ctx)
k.cancel = cancel
err := executor.StreamWithContext(ctx, remotecommand.StreamOptions{
Stdin: execconfig.Input,
Stdout: execconfig.Output,
Stderr: execconfig.Output,
Tty: execconfig.Tty,
TerminalSizeQueue: k,
})
exitCode := 0
if exitErr, ok := err.(exec.CodeExitError); ok {
exitCode = exitErr.ExitStatus()
}
r <- bridge.ExecResult{
ExitCode: exitCode,
Error: err,
}
}()
return r, nil
}
func (k *kubesshdconn) Resize(ctx context.Context, size bridge.ResizeOptions) error {
select {
case k.resizeQueue <- &remotecommand.TerminalSize{
Height: uint16(size.Height),
Width: uint16(size.Width),
}:
return nil
default:
}
return fmt.Errorf("resize failed")
}
func New(config *restclient.Config, namespace, pod, container string) (bridge.SessionProvider, error) {
return &kubesshdconn{
config: config,
resizeQueue: make(chan *remotecommand.TerminalSize, 1),
pod: pod,
namespace: namespace,
container: container,
}, nil
}