diff --git a/packet/Manager.py b/packet/Manager.py index 92999bb..bf36b5a 100644 --- a/packet/Manager.py +++ b/packet/Manager.py @@ -3,6 +3,7 @@ from packet.Vlan import Vlan from .baseapi import BaseAPI from .baseapi import Error as PacketError +from .baseapi import ResponseError from .Batch import Batch from .Plan import Plan from .Device import Device @@ -197,11 +198,46 @@ def create_ssh_key(self, label, public_key): return SSHKey(data, self) def create_project_ssh_key(self, project_id, label, public_key): - params = {"key": public_key, "label": label} - data = self.call_api( - "projects/%s/ssh-keys" % project_id, type="POST", params=params - ) - return SSHKey(data, self) + """ + Successfully creating an SSH key with a Project API Token results + in a 404 from the API. If we get a 404, we try the request again. + + If the request actually failed with a 404, we will get another 404 + which we raise. + + If the request actually succeeded, we will get a 422. In this case, + we will try to list all the keys and find the SSHKey we just + received. + + Customer Report Reference: TUVD-0107-UIKB + """ + + def issue_req(): + try: + params = {"key": public_key, "label": label} + data = self.call_api( + "projects/%s/ssh-keys" % project_id, type="POST", params=params + ) + return SSHKey(data, self) + except ResponseError as e: + if e.response.status_code == 422: + # Try to pluck the SSH key from the listing API + keys = [ + key + for key in self.list_ssh_keys() + if key.key.strip() == public_key.strip() + ] + if len(keys) == 1: + return keys.pop() + raise + + try: + return issue_req() + except ResponseError as e: + if e.response.status_code == 404: + return issue_req() + else: + raise def list_volumes(self, project_id, params={}): params["include"] = "facility,attachments.device" diff --git a/packet/__init__.py b/packet/__init__.py index 86864fc..1fba8c6 100644 --- a/packet/__init__.py +++ b/packet/__init__.py @@ -26,3 +26,4 @@ from .Organization import Organization # noqa from .Provider import Provider # noqa from .baseapi import Error # noqa +from .baseapi import ResponseError # noqa diff --git a/packet/baseapi.py b/packet/baseapi.py index 3e01d9e..c7c68c2 100644 --- a/packet/baseapi.py +++ b/packet/baseapi.py @@ -19,6 +19,21 @@ def cause(self): return self._cause +class ResponseError(Error): + def __init__(self, resp, data, exception=None): + if not data: + msg = "(empty response)" + elif "errors" in data: + msg = ", ".join(data["errors"]) + super().__init__("Error {0}: {1}".format(resp.status_code, msg), exception) + self._response = resp + + @property + def response(self): + """The Requests response which failed""" + return self._response + + class JSONReadError(Error): pass @@ -84,17 +99,12 @@ def call_api(self, method, type="GET", params=None): # noqa data = resp.content # pragma: no cover if not resp.ok: # pragma: no cover - msg = data - if not data: - msg = "(empty response)" - elif "errors" in data: - msg = ", ".join(data["errors"]) - raise Error("Error {0}: {1}".format(resp.status_code, msg)) + raise ResponseError(resp, data) try: resp.raise_for_status() except requests.HTTPError as e: # pragma: no cover - raise Error("Error {0}: {1}".format(resp.status_code, resp.reason), e) + raise ResponseError(resp, data, e) self.meta = None try: