12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
+ import http
15
16
import json
16
17
import pydoc
17
18
@@ -86,7 +87,7 @@ def get_watch_argument_name(self, func):
86
87
def unmarshal_event (self , data , return_type ):
87
88
js = json .loads (data )
88
89
js ['raw_object' ] = js ['object' ]
89
- if return_type :
90
+ if return_type and js [ 'type' ] != 'ERROR' :
90
91
obj = SimpleNamespace (data = json .dumps (js ['raw_object' ]))
91
92
js ['object' ] = self ._api_client .deserialize (obj , return_type )
92
93
if hasattr (js ['object' ], 'metadata' ):
@@ -102,6 +103,14 @@ def unmarshal_event(self, data, return_type):
102
103
def stream (self , func , * args , ** kwargs ):
103
104
"""Watch an API resource and stream the result back via a generator.
104
105
106
+ Note that watching an API resource can expire. The method tries to
107
+ resume automatically once from the last result, but if that last result
108
+ is too old as well, an `ApiException` exception will be thrown with
109
+ ``code`` 410. In that case you have to recover yourself, probably
110
+ by listing the API resource to obtain the latest state and then
111
+ watching from that state on by setting ``resource_version`` to
112
+ one returned from listing.
113
+
105
114
:param func: The API function pointer. Any parameter to the function
106
115
can be passed after this parameter.
107
116
@@ -134,14 +143,31 @@ def stream(self, func, *args, **kwargs):
134
143
self .resource_version = kwargs ['resource_version' ]
135
144
136
145
timeouts = ('timeout_seconds' in kwargs )
146
+ retry_after_410 = False
137
147
while True :
138
148
resp = func (* args , ** kwargs )
139
149
try :
140
150
for line in iter_resp_lines (resp ):
141
151
# unmarshal when we are receiving events from watch,
142
152
# return raw string when we are streaming log
143
153
if watch_arg == "watch" :
144
- yield self .unmarshal_event (line , return_type )
154
+ event = self .unmarshal_event (line , return_type )
155
+ if isinstance (event , dict ) \
156
+ and event ['type' ] == 'ERROR' :
157
+ obj = event ['raw_object' ]
158
+ # Current request expired, let's retry,
159
+ # but only if we have not already retried.
160
+ if not retry_after_410 and \
161
+ obj ['code' ] == http .HTTPStatus .GONE :
162
+ retry_after_410 = True
163
+ break
164
+ else :
165
+ reason = "%s: %s" % (obj ['reason' ], obj ['message' ])
166
+ raise client .rest .ApiException (status = obj ['code' ],
167
+ reason = reason )
168
+ else :
169
+ retry_after_410 = False
170
+ yield event
145
171
else :
146
172
yield line
147
173
if self ._stop :
0 commit comments