Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions packet/Manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions packet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
from .Organization import Organization # noqa
from .Provider import Provider # noqa
from .baseapi import Error # noqa
from .baseapi import ResponseError # noqa
24 changes: 17 additions & 7 deletions packet/baseapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down