server_websocket_test.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package server
  14. import (
  15. "encoding/binary"
  16. "fmt"
  17. "io"
  18. "strconv"
  19. "sync"
  20. "testing"
  21. "github.com/stretchr/testify/assert"
  22. "github.com/stretchr/testify/require"
  23. "golang.org/x/net/websocket"
  24. "k8s.io/apimachinery/pkg/types"
  25. "k8s.io/kubernetes/pkg/kubelet/server/portforward"
  26. )
  27. const (
  28. dataChannel = iota
  29. errorChannel
  30. )
  31. func TestServeWSPortForward(t *testing.T) {
  32. tests := map[string]struct {
  33. port string
  34. uid bool
  35. clientData string
  36. containerData string
  37. shouldError bool
  38. }{
  39. "no port": {port: "", shouldError: true},
  40. "none number port": {port: "abc", shouldError: true},
  41. "negative port": {port: "-1", shouldError: true},
  42. "too large port": {port: "65536", shouldError: true},
  43. "0 port": {port: "0", shouldError: true},
  44. "min port": {port: "1", shouldError: false},
  45. "normal port": {port: "8000", shouldError: false},
  46. "normal port with data forward": {port: "8000", clientData: "client data", containerData: "container data", shouldError: false},
  47. "max port": {port: "65535", shouldError: false},
  48. "normal port with uid": {port: "8000", uid: true, shouldError: false},
  49. }
  50. podNamespace := "other"
  51. podName := "foo"
  52. for desc, test := range tests {
  53. test := test
  54. t.Run(desc, func(t *testing.T) {
  55. ss, err := newTestStreamingServer(0)
  56. require.NoError(t, err)
  57. defer ss.testHTTPServer.Close()
  58. fw := newServerTestWithDebug(true, false, ss)
  59. defer fw.testHTTPServer.Close()
  60. portForwardFuncDone := make(chan struct{})
  61. fw.fakeKubelet.getPortForwardCheck = func(name, namespace string, uid types.UID, opts portforward.V4Options) {
  62. assert.Equal(t, podName, name, "pod name")
  63. assert.Equal(t, podNamespace, namespace, "pod namespace")
  64. if test.uid {
  65. assert.Equal(t, testUID, string(uid), "uid")
  66. }
  67. }
  68. ss.fakeRuntime.portForwardFunc = func(podSandboxID string, port int32, stream io.ReadWriteCloser) error {
  69. defer close(portForwardFuncDone)
  70. assert.Equal(t, testPodSandboxID, podSandboxID, "pod sandbox id")
  71. // The port should be valid if it reaches here.
  72. testPort, err := strconv.ParseInt(test.port, 10, 32)
  73. require.NoError(t, err, "parse port")
  74. assert.Equal(t, int32(testPort), port, "port")
  75. if test.clientData != "" {
  76. fromClient := make([]byte, 32)
  77. n, err := stream.Read(fromClient)
  78. assert.NoError(t, err, "reading client data")
  79. assert.Equal(t, test.clientData, string(fromClient[0:n]), "client data")
  80. }
  81. if test.containerData != "" {
  82. _, err := stream.Write([]byte(test.containerData))
  83. assert.NoError(t, err, "writing container data")
  84. }
  85. return nil
  86. }
  87. var url string
  88. if test.uid {
  89. url = fmt.Sprintf("ws://%s/portForward/%s/%s/%s?port=%s", fw.testHTTPServer.Listener.Addr().String(), podNamespace, podName, testUID, test.port)
  90. } else {
  91. url = fmt.Sprintf("ws://%s/portForward/%s/%s?port=%s", fw.testHTTPServer.Listener.Addr().String(), podNamespace, podName, test.port)
  92. }
  93. ws, err := websocket.Dial(url, "", "http://127.0.0.1/")
  94. assert.Equal(t, test.shouldError, err != nil, "websocket dial")
  95. if test.shouldError {
  96. return
  97. }
  98. defer ws.Close()
  99. p, err := strconv.ParseUint(test.port, 10, 16)
  100. require.NoError(t, err, "parse port")
  101. p16 := uint16(p)
  102. channel, data, err := wsRead(ws)
  103. require.NoError(t, err, "read")
  104. assert.Equal(t, dataChannel, int(channel), "channel")
  105. assert.Len(t, data, binary.Size(p16), "data size")
  106. assert.Equal(t, p16, binary.LittleEndian.Uint16(data), "data")
  107. channel, data, err = wsRead(ws)
  108. assert.NoError(t, err, "read")
  109. assert.Equal(t, errorChannel, int(channel), "channel")
  110. assert.Len(t, data, binary.Size(p16), "data size")
  111. assert.Equal(t, p16, binary.LittleEndian.Uint16(data), "data")
  112. if test.clientData != "" {
  113. println("writing the client data")
  114. err := wsWrite(ws, dataChannel, []byte(test.clientData))
  115. assert.NoError(t, err, "writing client data")
  116. }
  117. if test.containerData != "" {
  118. _, data, err = wsRead(ws)
  119. assert.NoError(t, err, "reading container data")
  120. assert.Equal(t, test.containerData, string(data), "container data")
  121. }
  122. <-portForwardFuncDone
  123. })
  124. }
  125. }
  126. func TestServeWSMultiplePortForward(t *testing.T) {
  127. portsText := []string{"7000,8000", "9000"}
  128. ports := []uint16{7000, 8000, 9000}
  129. podNamespace := "other"
  130. podName := "foo"
  131. ss, err := newTestStreamingServer(0)
  132. require.NoError(t, err)
  133. defer ss.testHTTPServer.Close()
  134. fw := newServerTestWithDebug(true, false, ss)
  135. defer fw.testHTTPServer.Close()
  136. portForwardWG := sync.WaitGroup{}
  137. portForwardWG.Add(len(ports))
  138. portsMutex := sync.Mutex{}
  139. portsForwarded := map[int32]struct{}{}
  140. fw.fakeKubelet.getPortForwardCheck = func(name, namespace string, uid types.UID, opts portforward.V4Options) {
  141. assert.Equal(t, podName, name, "pod name")
  142. assert.Equal(t, podNamespace, namespace, "pod namespace")
  143. }
  144. ss.fakeRuntime.portForwardFunc = func(podSandboxID string, port int32, stream io.ReadWriteCloser) error {
  145. defer portForwardWG.Done()
  146. assert.Equal(t, testPodSandboxID, podSandboxID, "pod sandbox id")
  147. portsMutex.Lock()
  148. portsForwarded[port] = struct{}{}
  149. portsMutex.Unlock()
  150. fromClient := make([]byte, 32)
  151. n, err := stream.Read(fromClient)
  152. assert.NoError(t, err, "reading client data")
  153. assert.Equal(t, fmt.Sprintf("client data on port %d", port), string(fromClient[0:n]), "client data")
  154. _, err = stream.Write([]byte(fmt.Sprintf("container data on port %d", port)))
  155. assert.NoError(t, err, "writing container data")
  156. return nil
  157. }
  158. url := fmt.Sprintf("ws://%s/portForward/%s/%s?", fw.testHTTPServer.Listener.Addr().String(), podNamespace, podName)
  159. for _, port := range portsText {
  160. url = url + fmt.Sprintf("port=%s&", port)
  161. }
  162. ws, err := websocket.Dial(url, "", "http://127.0.0.1/")
  163. require.NoError(t, err, "websocket dial")
  164. defer ws.Close()
  165. for i, port := range ports {
  166. channel, data, err := wsRead(ws)
  167. assert.NoError(t, err, "port %d read", port)
  168. assert.Equal(t, i*2+dataChannel, int(channel), "port %d channel", port)
  169. assert.Len(t, data, binary.Size(port), "port %d data size", port)
  170. assert.Equal(t, binary.LittleEndian.Uint16(data), port, "port %d data", port)
  171. channel, data, err = wsRead(ws)
  172. assert.NoError(t, err, "port %d read", port)
  173. assert.Equal(t, i*2+errorChannel, int(channel), "port %d channel", port)
  174. assert.Len(t, data, binary.Size(port), "port %d data size", port)
  175. assert.Equal(t, binary.LittleEndian.Uint16(data), port, "port %d data", port)
  176. }
  177. for i, port := range ports {
  178. t.Logf("port %d writing the client data", port)
  179. err := wsWrite(ws, byte(i*2+dataChannel), []byte(fmt.Sprintf("client data on port %d", port)))
  180. assert.NoError(t, err, "port %d write client data", port)
  181. channel, data, err := wsRead(ws)
  182. assert.NoError(t, err, "port %d read container data", port)
  183. assert.Equal(t, i*2+dataChannel, int(channel), "port %d channel", port)
  184. assert.Equal(t, fmt.Sprintf("container data on port %d", port), string(data), "port %d container data", port)
  185. }
  186. portForwardWG.Wait()
  187. portsMutex.Lock()
  188. defer portsMutex.Unlock()
  189. assert.Len(t, portsForwarded, len(ports), "all ports forwarded")
  190. }
  191. func wsWrite(conn *websocket.Conn, channel byte, data []byte) error {
  192. frame := make([]byte, len(data)+1)
  193. frame[0] = channel
  194. copy(frame[1:], data)
  195. err := websocket.Message.Send(conn, frame)
  196. return err
  197. }
  198. func wsRead(conn *websocket.Conn) (byte, []byte, error) {
  199. for {
  200. var data []byte
  201. err := websocket.Message.Receive(conn, &data)
  202. if err != nil {
  203. return 0, nil, err
  204. }
  205. if len(data) == 0 {
  206. continue
  207. }
  208. channel := data[0]
  209. data = data[1:]
  210. return channel, data, err
  211. }
  212. }