1
+ package io .kubernetes .client ;
2
+
3
+ import io .kubernetes .client .Configuration ;
4
+ import io .kubernetes .client .models .V1Pod ;
5
+ import io .kubernetes .client .util .WebSockets ;
6
+ import io .kubernetes .client .util .WebSocketStreamHandler ;
7
+
8
+ import java .io .InputStream ;
9
+ import java .io .IOException ;
10
+ import java .io .OutputStream ;
11
+ import java .util .ArrayList ;
12
+ import java .util .HashMap ;
13
+ import java .util .List ;
14
+
15
+ /**
16
+ * Utility class for setting up port-forwarding connections.
17
+ * Uses the WebSockets API, not the SPDY API (which the Go client uses)
18
+ *
19
+ * The protocol is undocumented as far as I can tell, but the PR that added
20
+ * it is here:
21
+ * https://github.com/kubernetes/kubernetes/pull/33684
22
+ *
23
+ * And the protocol is:
24
+ *
25
+ * ws://server/api/v1/namespaces/<namespace>/pods/<pod>/portforward?ports=80&ports=8080
26
+ *
27
+ * I/O for first port (80) is on Channel 0
28
+ * Err for first port (80) is on Channel 1
29
+ * I/O for second port (8080) is on Channel 2
30
+ * Err for second port (8080) is on Channel 3
31
+ * <and so on for remaining ports>
32
+ *
33
+ * The first two bytes of each output stream is the port that is being forwarded
34
+ * in little-endian format.
35
+ */
36
+ public class PortForward {
37
+ private ApiClient apiClient ;
38
+
39
+ /**
40
+ * Simple PortForward API constructor, uses default configuration
41
+ */
42
+ public PortForward () {
43
+ this (Configuration .getDefaultApiClient ());
44
+ }
45
+
46
+ /**
47
+ * PortForward API Constructor
48
+ * @param apiClient The api client to use.
49
+ */
50
+ public PortForward (ApiClient apiClient ) {
51
+ this .apiClient = apiClient ;
52
+ }
53
+
54
+ /**
55
+ * Get the API client for these PortForward operations.
56
+ * @return The API client that will be used.
57
+ */
58
+ public ApiClient getApiClient () {
59
+ return apiClient ;
60
+ }
61
+
62
+ /**
63
+ * Set the API client for subsequent PortForward operations.
64
+ * @param apiClient The new API client to use.
65
+ */
66
+ public void setApiClient (ApiClient apiClient ) {
67
+ this .apiClient = apiClient ;
68
+ }
69
+
70
+ private String makePath (String namespace , String name ) {
71
+ return "/api/v1/namespaces/" +
72
+ namespace +
73
+ "/pods/" +
74
+ name +
75
+ "/portforward" ;
76
+ }
77
+
78
+ /**
79
+ * PortForward to a container
80
+ *
81
+ * @param pod The pod where the port forward is run.
82
+ * @param ports The ports to forward
83
+ * @return The result of the Port Forward request.
84
+ */
85
+ public PortForwardResult forward (V1Pod pod , List <Integer > ports ) throws ApiException , IOException {
86
+ return forward (pod .getMetadata ().getNamespace (), pod .getMetadata ().getNamespace (), ports );
87
+ }
88
+
89
+ /**
90
+ * PortForward to a container.
91
+ *
92
+ * @param namespace The namespace of the Pod
93
+ * @param name The name of the Pod
94
+ * @param ports The ports to forward
95
+ * @return The result of the Port Forward request.
96
+ */
97
+ public PortForwardResult forward (String namespace , String name , List <Integer > ports ) throws ApiException , IOException {
98
+ String path = makePath (namespace , name );
99
+ WebSocketStreamHandler handler = new WebSocketStreamHandler ();
100
+ PortForwardResult result = new PortForwardResult (handler , ports );
101
+ List <Pair > queryParams = new ArrayList <>();
102
+ for (Integer port : ports ) {
103
+ queryParams .add (new Pair ("ports" , port .toString ()));
104
+ }
105
+ WebSockets .stream (path , "GET" , queryParams , apiClient , handler );
106
+
107
+ // Wait for streams to start.
108
+ result .init ();
109
+
110
+ return result ;
111
+ }
112
+
113
+ /**
114
+ * PortForwardResult contains the result of an Attach call, it includes streams for stdout
115
+ * stderr and stdin.
116
+ */
117
+ public static class PortForwardResult {
118
+ private WebSocketStreamHandler handler ;
119
+ private HashMap <Integer , Integer > streams ;
120
+ private List <Integer > ports ;
121
+
122
+ /**
123
+ * Constructor
124
+ * @param handler The web socket handler
125
+ * @param ports The list of ports that are being forwarded.
126
+ */
127
+ public PortForwardResult (WebSocketStreamHandler handler , List <Integer > ports ) throws IOException {
128
+ this .handler = handler ;
129
+ this .streams = new HashMap <>();
130
+ this .ports = ports ;
131
+ }
132
+
133
+ /**
134
+ * Initialize the connection. Must be called after the web socket has been opened.
135
+ */
136
+ public void init () throws IOException {
137
+ for (int i = 0 ; i < ports .size (); i ++) {
138
+ InputStream is = handler .getInputStream (i );
139
+ byte [] data = new byte [2 ];
140
+ is .read (data );
141
+ int port = data [0 ] + data [1 ] * 256 ;
142
+ streams .put (port , i );
143
+ }
144
+ }
145
+
146
+ private int findPortIndex (int portNumber ) {
147
+ Integer ix = streams .get (portNumber );
148
+ if (ix == null ) {
149
+ return -1 ;
150
+ }
151
+ return ix .intValue ();
152
+ }
153
+
154
+ /**
155
+ * Get the output stream for the specified port number (e.g. 80)
156
+ * @param port The port number to get the stream for.
157
+ * @return The OutputStream for the specified port, null if there is no such port.
158
+ */
159
+ public OutputStream getOutboundStream (int port ) {
160
+ int portIndex = findPortIndex (port );
161
+ if (portIndex == -1 ) {
162
+ return null ;
163
+ }
164
+ return handler .getOutputStream (portIndex * 2 );
165
+ }
166
+
167
+ /**
168
+ * Get the error stream for a port number (e.g. 80)
169
+ * @param port The port number to get the stream for.
170
+ * @return The error stream, or null if there is no such port.
171
+ */
172
+ public OutputStream getErrorStream (int port ) {
173
+ int portIndex = findPortIndex (port );
174
+ if (portIndex == -1 ) {
175
+ return null ;
176
+ }
177
+ return handler .getOutputStream (portIndex * 2 + 1 );
178
+ }
179
+
180
+ /**
181
+ * Get the input stream for a port number (e.g. 80)
182
+ * @param port The port number to get the stream for.
183
+ * @return The input stream, or null if no such port exists.
184
+ */
185
+ public InputStream getInputStream (int port ) throws IOException {
186
+ int portIndex = findPortIndex (port );
187
+ if (portIndex == -1 ) {
188
+ return null ;
189
+ }
190
+ return handler .getInputStream (portIndex * 2 );
191
+ }
192
+ }
193
+ }
0 commit comments