-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
148 lines (122 loc) · 3.04 KB
/
main.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package main
import (
"bufio"
"crypto/tls"
"fmt"
"log"
"net/http"
"os"
"github.com/gorilla/websocket"
)
var (
token string
addr string
sc = bufio.NewScanner(os.Stdin)
uriFormat = "wss://%s/api/v1/namespaces/%s/pods/%s/exec?command=/bin/sh&stdin=true&stderr=true&stdout=true&tty=true"
)
func init() {
// 下記コマンドでトークンを取得:
// SECRET_NAME=$(kubectl get serviceaccount default -o jsonpath='{.secrets[0].name}')
// TOKEN=$(kubectl get secret $SECRET_NAME -o jsonpath='{.data.token}' | base64 --decode)
// echo $TOKEN
if token = os.Getenv("K8S_TOKEN"); token == "" {
fmt.Println("error: K8S_TOKEN value is empty.")
os.Exit(0)
}
// 下記コマンドでk8sアドレスを取得:
// kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'
if addr = os.Getenv("K8S_ADDR"); addr == "" {
fmt.Println("error: K8S_ADDR value is empty.")
os.Exit(0)
}
}
type k8sSock struct {
token string
addr string
uri string
}
func newClient() k8sSock {
return k8sSock{
token: token,
addr: addr,
}
}
func (k8s *k8sSock) URI(namespace, pod string) {
k8s.uri = fmt.Sprintf(uriFormat, k8s.addr, namespace, pod)
}
func (k8s *k8sSock) Start() error {
requestHeader := newRequestHeader(k8s.token)
dialer := websocket.DefaultDialer
dialer.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
// Connect to target kubernetes
conn, _, err := dialer.Dial(k8s.uri, requestHeader)
if err != nil {
return err
}
defer conn.Close()
// error channel
errOutChan := make(chan error, 1)
errInChan := make(chan error, 1)
// 手元のコンソールからの入力待ち
go outputListener(conn, errOutChan)
// k8sコンテナからの出力待ち
go inputListener(conn, errInChan)
var message string
select {
case err = <-errOutChan:
message = "error: %v\n"
case err = <-errInChan:
message = "error: %v\n"
}
if e, ok := err.(*websocket.CloseError); !ok || e.Code == websocket.CloseAbnormalClosure {
log.Printf(message, err)
}
return nil
}
func newRequestHeader(token string) http.Header {
requestHeader := http.Header{}
requestHeader.Add("Authorization", "Bearer "+token)
return requestHeader
}
func outputListener(conn *websocket.Conn, errChan chan error) {
for {
cmd := func() []byte {
sc.Scan()
cmd := append([]byte{0}, sc.Bytes()...)
return append(cmd, byte(13))
}()
if err := conn.WriteMessage(websocket.BinaryMessage, cmd); err != nil {
errChan <- err
}
}
}
func inputListener(conn *websocket.Conn, errChan chan error) {
for {
_, msg, err := conn.ReadMessage()
if err != nil {
conn.WriteMessage(websocket.CloseMessage, []byte(err.Error()))
errChan <- err
break
}
if len(msg) > 1 {
fmt.Printf(fmt.Sprintf("%s", msg[1:len(msg)]))
}
}
}
func main() {
readLine := func() string {
sc.Scan()
return sc.Text()
}
fmt.Print("Kubernetes namespace: ")
namespace := readLine()
fmt.Print("Kubernetes pod name: ")
podname := readLine()
sock := newClient()
sock.URI(namespace, podname)
if err := sock.Start(); err != nil {
log.Fatal(err)
}
}