29
29
30
30
class WSClient :
31
31
def __init__ (self , configuration , url , headers ):
32
+ """A websocket client with support for channels.
33
+
34
+ Exec command uses different channels for different streams. for
35
+ example, 0 is stdin, 1 is stdout and 2 is stderr. Some other API calls
36
+ like port forwarding can forward different pods' streams to different
37
+ channels.
38
+ """
32
39
enableTrace (False )
33
40
header = []
34
41
self ._connected = False
@@ -37,7 +44,7 @@ def __init__(self, configuration, url, headers):
37
44
38
45
# We just need to pass the Authorization, ignore all the other
39
46
# http headers we get from the generated code
40
- if 'Authorization' in headers :
47
+ if headers and 'Authorization' in headers :
41
48
header .append ("Authorization: %s" % headers ['Authorization' ])
42
49
43
50
if url .startswith ('wss://' ) and configuration .verify_ssl :
@@ -57,12 +64,15 @@ def __init__(self, configuration, url, headers):
57
64
self ._connected = True
58
65
59
66
def peek_channel (self , channel , timeout = 0 ):
67
+ """Peek a channel and return part of the input,
68
+ empty string otherwise."""
60
69
self .update (timeout = timeout )
61
70
if channel in self ._channels :
62
71
return self ._channels [channel ]
63
72
return ""
64
73
65
74
def read_channel (self , channel , timeout = 0 ):
75
+ """Read data from a channel."""
66
76
if channel not in self ._channels :
67
77
ret = self .peek_channel (channel , timeout )
68
78
else :
@@ -72,6 +82,7 @@ def read_channel(self, channel, timeout=0):
72
82
return ret
73
83
74
84
def readline_channel (self , channel , timeout = None ):
85
+ """Read a line from a channel."""
75
86
if timeout is None :
76
87
timeout = float ("inf" )
77
88
start = time .time ()
@@ -90,39 +101,57 @@ def readline_channel(self, channel, timeout=None):
90
101
self .update (timeout = (timeout - time .time () + start ))
91
102
92
103
def write_channel (self , channel , data ):
104
+ """Write data to a channel."""
93
105
self .sock .send (chr (channel ) + data )
94
106
95
107
def peek_stdout (self , timeout = 0 ):
108
+ """Same as peek_channel with channel=1."""
96
109
return self .peek_channel (STDOUT_CHANNEL , timeout = timeout )
97
110
98
111
def read_stdout (self , timeout = None ):
112
+ """Same as read_channel with channel=1."""
99
113
return self .read_channel (STDOUT_CHANNEL , timeout = timeout )
100
114
101
115
def readline_stdout (self , timeout = None ):
116
+ """Same as readline_channel with channel=1."""
102
117
return self .readline_channel (STDOUT_CHANNEL , timeout = timeout )
103
118
104
119
def peek_stderr (self , timeout = 0 ):
120
+ """Same as peek_channel with channel=2."""
105
121
return self .peek_channel (STDERR_CHANNEL , timeout = timeout )
106
122
107
123
def read_stderr (self , timeout = None ):
124
+ """Same as read_channel with channel=2."""
108
125
return self .read_channel (STDERR_CHANNEL , timeout = timeout )
109
126
110
127
def readline_stderr (self , timeout = None ):
128
+ """Same as readline_channel with channel=2."""
111
129
return self .readline_channel (STDERR_CHANNEL , timeout = timeout )
112
130
113
131
def read_all (self ):
132
+ """Read all of the inputs with the same order they recieved. The channel
133
+ information would be part of the string. This is useful for
134
+ non-interactive call where a set of command passed to the API call and
135
+ their result is needed after the call is concluded.
136
+
137
+ TODO: Maybe we can process this and return a more meaningful map with
138
+ channels mapped for each input.
139
+ """
114
140
out = self ._all
115
141
self ._all = ""
116
142
self ._channels = {}
117
143
return out
118
144
119
145
def is_open (self ):
146
+ """True if the connection is still alive."""
120
147
return self ._connected
121
148
122
149
def write_stdin (self , data ):
150
+ """The same as write_channel with channel=0."""
123
151
self .write_channel (STDIN_CHANNEL , data )
124
152
125
153
def update (self , timeout = 0 ):
154
+ """Update channel buffers with at most one complete frame of input."""
126
155
if not self .is_open ():
127
156
return
128
157
if not self .sock .connected :
@@ -150,6 +179,8 @@ def update(self, timeout=0):
150
179
self ._channels [channel ] += data
151
180
152
181
def run_forever (self , timeout = None ):
182
+ """Wait till connection is closed or timeout reached. Buffer any input
183
+ received during this time."""
153
184
if timeout :
154
185
start = time .time ()
155
186
while self .is_open () and time .time () - start < timeout :
@@ -162,8 +193,11 @@ def run_forever(self, timeout=None):
162
193
WSResponse = collections .namedtuple ('WSResponse' , ['data' ])
163
194
164
195
165
- def GET (configuration , url , query_params , _request_timeout , _preload_content ,
166
- headers ):
196
+ def websocket_call (configuration , url , query_params , _request_timeout ,
197
+ _preload_content , headers ):
198
+ """An internal function to be called in api-client when a websocket
199
+ connection is required."""
200
+
167
201
# switch protocols from http to websocket
168
202
url = url .replace ('http://' , 'ws://' )
169
203
url = url .replace ('https://' , 'wss://' )
0 commit comments