1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+ import http
1516import json
1617import pydoc
1718
@@ -86,7 +87,7 @@ def get_watch_argument_name(self, func):
8687 def unmarshal_event (self , data , return_type ):
8788 js = json .loads (data )
8889 js ['raw_object' ] = js ['object' ]
89- if return_type :
90+ if return_type and js [ 'type' ] != 'ERROR' :
9091 obj = SimpleNamespace (data = json .dumps (js ['raw_object' ]))
9192 js ['object' ] = self ._api_client .deserialize (obj , return_type )
9293 if hasattr (js ['object' ], 'metadata' ):
@@ -102,6 +103,14 @@ def unmarshal_event(self, data, return_type):
102103 def stream (self , func , * args , ** kwargs ):
103104 """Watch an API resource and stream the result back via a generator.
104105
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+
105114 :param func: The API function pointer. Any parameter to the function
106115 can be passed after this parameter.
107116
@@ -134,14 +143,31 @@ def stream(self, func, *args, **kwargs):
134143 self .resource_version = kwargs ['resource_version' ]
135144
136145 timeouts = ('timeout_seconds' in kwargs )
146+ retry_after_410 = False
137147 while True :
138148 resp = func (* args , ** kwargs )
139149 try :
140150 for line in iter_resp_lines (resp ):
141151 # unmarshal when we are receiving events from watch,
142152 # return raw string when we are streaming log
143153 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
145171 else :
146172 yield line
147173 if self ._stop :
0 commit comments