Skip to content

Commit 5f07dc6

Browse files
committed
Add interfaces for client-side string upload
Issues: Fixes #454 Problem: We need to upload secrets without writing them to a file on the client side. Tests: unittests in test_file_uploads.py
1 parent d5c8305 commit 5f07dc6

4 files changed

Lines changed: 94 additions & 28 deletions

File tree

f5/bigip/cm/autodeploy/software_images.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def upload_image(self, filepathname, **kwargs):
3838
if os.path.splitext(filename)[-1] != '.iso':
3939
raise ImageFilesMustHaveDotISOExtension(filename)
4040
self.file_bound_uri = self._meta_data['uri'] + filename
41-
self._upload(filepathname, **kwargs)
41+
self._upload_file(filepathname, **kwargs)
4242
#
4343
#
4444
# class Software_Image_Downloads(PathElement):

f5/bigip/mixins.py

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
# NOTE: Code taken from Effective Python Item 26
1818

1919
import logging
20-
import os
2120

2221
from f5.sdk_exception import F5SDKError
2322

@@ -243,34 +242,40 @@ def _exec_cmd(self, command, **kwargs):
243242

244243

245244
class FileUploadMixin(object):
246-
def _upload(self, filepathname, **kwargs):
245+
def _upload_file(self, filepathname, **kwargs):
246+
with open(filepathname, 'rb') as fileobj:
247+
self._upload(fileobj, **kwargs)
248+
249+
def _upload(self, fileinterface, **kwargs):
250+
size = len(fileinterface.read())
251+
fileinterface.seek(0)
247252
requests_params = self._handle_requests_params(kwargs)
248253
session = self._meta_data['icr_session']
249254
chunk_size = kwargs.pop('chunk_size', 512 * 1024)
250-
size = os.path.getsize(filepathname)
251255
start = 0
252-
with open(filepathname, 'rb') as fileobj:
253-
while True:
254-
file_slice = fileobj.read(chunk_size)
255-
if not file_slice:
256-
break
257-
258-
current_bytes = len(file_slice)
259-
if current_bytes < chunk_size:
260-
end = size
261-
else:
262-
end = start + current_bytes
263-
headers = {
264-
'Content-Range': '%s-%s/%s' % (start, end - 1, size),
265-
'Content-Type': 'application/octet-stream'}
266-
data = {'data': file_slice,
267-
'headers': headers,
268-
'verify': False}
269-
logging.debug(data)
270-
requests_params.update(data)
271-
session.post(self.file_bound_uri,
272-
**requests_params)
273-
start += current_bytes
256+
while True:
257+
file_slice = fileinterface.read(chunk_size)
258+
if not file_slice:
259+
break
260+
261+
current_bytes = len(file_slice)
262+
if current_bytes < chunk_size:
263+
end = size
264+
else:
265+
end = start + current_bytes
266+
headers = {
267+
'Content-Range': '%s-%s/%s' % (start,
268+
end - 1,
269+
size),
270+
'Content-Type': 'application/octet-stream'}
271+
data = {'data': file_slice,
272+
'headers': headers,
273+
'verify': False}
274+
logging.debug(data)
275+
requests_params.update(data)
276+
session.post(self.file_bound_uri,
277+
**requests_params)
278+
start += current_bytes
274279

275280

276281
class DeviceMixin(object):

f5/bigip/shared/file_transfer.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#
1818

1919
import os
20+
from StringIO import StringIO
2021

2122
from f5.bigip.mixins import FileUploadMixin
2223
from f5.bigip.resource import PathElement
@@ -47,4 +48,12 @@ def upload_file(self, filepathname, **kwargs):
4748
if os.path.splitext(filename)[-1] == '.iso':
4849
raise FileMustNotHaveDotISOExtension(filename)
4950
self.file_bound_uri = self._meta_data['uri'] + filename
50-
self._upload(filepathname, **kwargs)
51+
self._upload_file(filepathname, **kwargs)
52+
53+
def upload_stringio(self, stringio, target, **kwargs):
54+
self.file_bound_uri = self._meta_data['uri'] + target
55+
self._upload(stringio, **kwargs)
56+
57+
def upload_bytes(self, bytestring, target, **kwargs):
58+
self.file_bound_uri = self._meta_data['uri'] + target
59+
self._upload(StringIO(bytestring), **kwargs)

f5/bigip/shared/test/test_file_uploads.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
#
1515

1616
import mock
17-
1817
import pytest
18+
from StringIO import StringIO
1919

2020
from f5.bigip import ManagementRoot
2121
from f5.bigip.shared.file_transfer import\
@@ -59,3 +59,55 @@ def test_ISO_extension(tmpdir):
5959
with pytest.raises(FileMustNotHaveDotISOExtension) as EIO:
6060
ftu.upload_file(filepath.__str__(), chunk_size=21)
6161
assert EIO.value.message == 'wrongname.iso'
62+
63+
64+
def test_stringio_upload_80a(tmpdir):
65+
sio = StringIO(80*'a')
66+
mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD')
67+
mr._meta_data['icr_session'] = mock.MagicMock()
68+
ftu = mr.shared.file_transfer.uploads
69+
ftu.upload_stringio(sio, 'testtarget', chunk_size=20)
70+
session_mock = mr._meta_data['icr_session']
71+
for i in range(4):
72+
d = session_mock.post.call_args_list[i][1]['data']
73+
assert d == 'aaaaaaaaaaaaaaaaaaaa'
74+
75+
76+
def test_stringio_upload_70a(tmpdir):
77+
sio = StringIO(70*'a')
78+
mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD')
79+
mr._meta_data['icr_session'] = mock.MagicMock()
80+
ftu = mr.shared.file_transfer.uploads
81+
ftu.upload_stringio(sio, 'testtarget', chunk_size=20)
82+
session_mock = mr._meta_data['icr_session']
83+
for i in range(3):
84+
d = session_mock.post.call_args_list[i][1]['data']
85+
assert d == 'aaaaaaaaaaaaaaaaaaaa'
86+
lchunk = session_mock.post.call_args_list[3][1]['data']
87+
assert 10*'a' == lchunk
88+
89+
90+
def test_bytes_upload_80a(tmpdir):
91+
bytestring80a = 80*'a'
92+
mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD')
93+
mr._meta_data['icr_session'] = mock.MagicMock()
94+
ftu = mr.shared.file_transfer.uploads
95+
ftu.upload_bytes(bytestring80a, 'testtarget', chunk_size=20)
96+
session_mock = mr._meta_data['icr_session']
97+
for i in range(4):
98+
d = session_mock.post.call_args_list[i][1]['data']
99+
assert d == 'aaaaaaaaaaaaaaaaaaaa'
100+
101+
102+
def test_bytes_upload_70a(tmpdir):
103+
bytestring70a = 70*'a'
104+
mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD')
105+
mr._meta_data['icr_session'] = mock.MagicMock()
106+
ftu = mr.shared.file_transfer.uploads
107+
ftu.upload_bytes(bytestring70a, 'testtarget', chunk_size=20)
108+
session_mock = mr._meta_data['icr_session']
109+
for i in range(3):
110+
d = session_mock.post.call_args_list[i][1]['data']
111+
assert d == 'aaaaaaaaaaaaaaaaaaaa'
112+
lchunk = session_mock.post.call_args_list[3][1]['data']
113+
assert 10*'a' == lchunk

0 commit comments

Comments
 (0)