Repository: tomplus/kubernetes_asyncio
Pull request #154 (2021-07-27 20:51:39)

fix: handle 401 returned by GKE
Fixes https://github.com/tomplus/kubernetes_asyncio/issues/150

codecov[bot]: # [Codecov](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus) Report
> Merging [#154](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus) (2136792) into [master](https://codecov.io/gh/tomplus/kubernetes_asyncio/commit/89944de1b3ff87527bb2e6639bcd96cc119cc647?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus) (89944de) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154/graphs/tree.svg?width=650&height=150&src=pr&token=0Dvm4fqopq&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus)](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus)

```diff
@@           Coverage Diff           @@
##           master     #154   +/-   ##
=======================================
  Coverage   31.18%   31.18%           
=======================================
  Files         817      817           
  Lines       93206    93213    +7     
=======================================
+ Hits        29064    29071    +7     
  Misses      64142    64142           
```


| [Impacted Files](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus) | Coverage Δ | |
|---|---|---|
| [kubernetes\_asyncio/watch/watch.py](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus#diff-a3ViZXJuZXRlc19hc3luY2lvL3dhdGNoL3dhdGNoLnB5) | `95.19% <100.00%> (+0.14%)` | :arrow_up: |
| [kubernetes\_asyncio/watch/watch\_test.py](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus#diff-a3ViZXJuZXRlc19hc3luY2lvL3dhdGNoL3dhdGNoX3Rlc3QucHk=) | `98.55% <100.00%> (+0.04%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus). Last update [89944de...2136792](https://codecov.io/gh/tomplus/kubernetes_asyncio/pull/154?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tomasz+Prus).


Patch:
diff --git a/kubernetes_asyncio/watch/watch.py b/kubernetes_asyncio/watch/watch.py
index fbc04270..3a86e015 100644
--- a/kubernetes_asyncio/watch/watch.py
+++ b/kubernetes_asyncio/watch/watch.py
@@ -81,6 +81,11 @@ class Watch(object):
             return data
 
         if 'object' not in js or 'type' not in js:
+            # raise error with code if set
+            if 'code' in js:
+                reason = "{}: {}".format(js.get('reason'), js.get('message'))
+                raise client.exceptions.ApiException(status=js['code'], reason=reason)
+
             raise Exception(("Malformed JSON response, the 'object' and/or "
                              "'type' field is missing. JSON: {}").format(js))
         # Make a copy of the original object and save it under the
@@ -94,7 +99,7 @@ class Watch(object):
         # this error into a Python exception to save the user the hassle.
         if js['type'].lower() == 'error':
             obj = js['raw_object']
-            reason = "%s: %s" % (obj['reason'], obj['message'])
+            reason = "{}: {}".format(obj['reason'], obj['message'])
             raise client.exceptions.ApiException(status=obj['code'], reason=reason)
 
         # If possible, compile the JSON response into a Python native response


Test patch:
diff --git a/kubernetes_asyncio/watch/watch_test.py b/kubernetes_asyncio/watch/watch_test.py
index 7ea9edc3..1cac4832 100644
--- a/kubernetes_asyncio/watch/watch_test.py
+++ b/kubernetes_asyncio/watch/watch_test.py
@@ -154,7 +154,9 @@ class WatchTest(TestCase):
         k8s_err = {
             'type': 'ERROR',
             'object': {
-                'kind': 'Status', 'apiVersion': 'v1', 'metadata': {},
+                'kind': 'Status',
+                'apiVersion': 'v1',
+                'metadata': {},
                 'status': 'Failure',
                 'message': 'too old resource version: 1 (8146471)',
                 'reason': 'Gone',
@@ -167,6 +169,28 @@ class WatchTest(TestCase):
                 r'\(410\)\nReason: Gone: too old resource version: 1 \(8146471\)'):
             Watch().unmarshal_event(json.dumps(k8s_err), None)
 
+    async def test_unmarshall_k8s_error_response_401_gke(self):
+        """Never parse messages of type ERROR.
+
+        This test uses an actually recorded error returned by GKE.
+
+        """
+        # An actual error response sent by K8s during testing.
+        k8s_err = {
+            'kind': 'Status',
+            'apiVersion': 'v1',
+            'metadata': {},
+            'status': 'Failure',
+            'message': 'Unauthorized',
+            'reason': 'Unauthorized',
+            'code': 401
+        }
+
+        with self.assertRaisesRegex(
+                kubernetes_asyncio.client.exceptions.ApiException,
+                r'\(401\)\nReason: Unauthorized: Unauthorized'):
+            Watch().unmarshal_event(json.dumps(k8s_err), None)
+
     def test_unmarshal_with_custom_object(self):
         w = Watch()
         event = w.unmarshal_event('{"type": "ADDED", "object": {"apiVersion":'