Merge ~ack/maas:1951398-cli-encode-as-file-3.1 into maas:3.1

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: f1e792df727eb623cca45544b2ba3a05b90a62c9
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:1951398-cli-encode-as-file-3.1
Merge into: maas:3.1
Diff against target: 84 lines (+19/-17)
2 files modified
src/maascli/api.py (+16/-15)
src/maascli/tests/test_api.py (+3/-2)
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
MAAS Lander Approve
Review via email: mp+412072@code.launchpad.net

Commit message

LP:1951398 keep @= parameters as uploaded files for POST actions

When @= is used in the CLI to read a parameter from file, only read the content
directly if the action is GET or DELETE, otherwise pass it as a file, so that
it gets multipart-encoded correctly (the filename= parameter is set in the
Content-Disposition header).

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b 1951398-cli-encode-as-file-3.1 lp:~ack/maas/+git/maas into -b 3.1 lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: f1e792df727eb623cca45544b2ba3a05b90a62c9

review: Approve
Revision history for this message
Alberto Donato (ack) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/maascli/api.py b/src/maascli/api.py
index a82f5c7..c533f1f 100644
--- a/src/maascli/api.py
+++ b/src/maascli/api.py
@@ -198,7 +198,7 @@ class Action(Command):
198 if what == "=":198 if what == "=":
199 return name, value199 return name, value
200 elif what == "@=":200 elif what == "@=":
201 return name, partial(open, value)201 return name, partial(open, value, "rb")
202 else:202 else:
203 raise AssertionError("Unrecognised separator %r" % what)203 raise AssertionError("Unrecognised separator %r" % what)
204 else:204 else:
@@ -222,25 +222,26 @@ class Action(Command):
222 tuples (see `name_value_pair`) to pack into the body or222 tuples (see `name_value_pair`) to pack into the body or
223 query, depending on the type of request.223 query, depending on the type of request.
224 """224 """
225 params_in_qs = method in ("GET", "DELETE")
226 query = [("op", op)] if op else []225 query = [("op", op)] if op else []
227226
227 headers, body = [], None
228
228 def slurp(opener):229 def slurp(opener):
229 with opener("r" if params_in_qs else "rb") as fd:230 with opener() as fd:
230 return fd.read()231 return fd.read()
231232
232 # read content for file-based parameters233 if method in ("GET", "DELETE"):
233 data = [234 query.extend(
234 (key, slurp(value) if callable(value) else value)235 (name, slurp(value) if callable(value) else value)
235 for key, value in data236 for name, value in data
236 ]237 )
237238 body, headers = None, []
238 headers, body = [], None239 else:
239 if params_in_qs:240 if data is None or len(data) == 0:
240 query.extend(data)241 body, headers = None, []
241 elif data:242 else:
242 message = build_multipart_message(data)243 message = build_multipart_message(data)
243 headers, body = encode_multipart_message(message)244 headers, body = encode_multipart_message(message)
244245
245 uri = urlparse(uri)._replace(query=urlencode(query)).geturl()246 uri = urlparse(uri)._replace(query=urlencode(query)).geturl()
246 return uri, body, headers247 return uri, body, headers
diff --git a/src/maascli/tests/test_api.py b/src/maascli/tests/test_api.py
index 402bdc2..12ae2c3 100644
--- a/src/maascli/tests/test_api.py
+++ b/src/maascli/tests/test_api.py
@@ -2,6 +2,7 @@
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4from base64 import b64encode4from base64 import b64encode
5from functools import partial
5import http.client6import http.client
6import json7import json
7import sys8import sys
@@ -698,7 +699,7 @@ class TestPayloadPreparationWithFiles:
698 # command-line causes name_value_pair() to return a `name,699 # command-line causes name_value_pair() to return a `name,
699 # opener` tuple, where `opener` is a callable that returns an700 # opener` tuple, where `opener` is a callable that returns an
700 # open file handle.701 # open file handle.
701 data = [(parameter, payload_file.open)]702 data = [(parameter, partial(payload_file.open, "rb"))]
702 return parameter, contents, data703 return parameter, contents, data
703704
704 @pytest.mark.parametrize("op", [None, "action"])705 @pytest.mark.parametrize("op", [None, "action"])
@@ -718,7 +719,7 @@ class TestPayloadPreparationWithFiles:
718 "Content-Type: application/octet-stream",719 "Content-Type: application/octet-stream",
719 "MIME-Version: 1.0",720 "MIME-Version: 1.0",
720 "Content-Transfer-Encoding: base64",721 "Content-Transfer-Encoding: base64",
721 f'Content-Disposition: form-data; name="{parameter}"',722 f'Content-Disposition: form-data; name="{parameter}"; filename="{parameter}"',
722 b64encode(contents).decode("ascii"),723 b64encode(contents).decode("ascii"),
723 ]724 ]
724 for line in content_lines:725 for line in content_lines:

Subscribers

People subscribed via source and target branches