Handle binary userdata files such as gzip

The current code in the servers api assumes only text files will be
provided as userdata. That is, the data is utf-8 encoded before it
is base64 encoded and sent. This breaks for binary userdata files,
(example, gzip files) as they can't be utf-8 encoded.

This change ignores specifically the exceptions that are raised
when utf-8 encoding is attempted on non-text data: AttributeError
and UnicodeDecodeError. These exceptions will be ignored to let
binary files proceed to the base64 encoding step.

Closes-Bug: #1419859

Change-Id: Ie96283f6ff892ae30485722cf7c3ec40f3f1b5df
This commit is contained in:
melanie witt 2015-04-03 08:52:42 +00:00
parent 3681c186f7
commit 19d4d35a4e
2 changed files with 41 additions and 2 deletions
novaclient

@ -12,6 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import base64
import os
import tempfile
import mock
from oslo.serialization import jsonutils
import six
@ -199,6 +203,32 @@ class ServersTest(utils.FixturedTestCase):
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_userdata_bin(self):
with tempfile.TemporaryFile(mode='wb+') as bin_file:
original_data = os.urandom(1024)
bin_file.write(original_data)
bin_file.flush()
bin_file.seek(0)
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
meta={'foo': 'bar'},
userdata=bin_file,
key_name="fakekey",
files={
'/etc/passwd': 'some data', # a file
'/tmp/foo.txt': six.StringIO('data'), # a stream
},
)
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
# verify userdata matches original
body = jsonutils.loads(self.requests.last_request.body)
transferred_data = body['server']['user_data']
transferred_data = base64.b64decode(transferred_data)
self.assertEqual(original_data, transferred_data)
def _create_disk_config(self, disk_config):
s = self.cs.servers.create(
name="My server",

@ -433,10 +433,19 @@ class ServerManager(base.BootingManagerWithFind):
if hasattr(userdata, 'read'):
userdata = userdata.read()
# NOTE(melwitt): Text file data is converted to bytes prior to
# base64 encoding. The utf-8 encoding will fail for binary files.
if six.PY3:
userdata = userdata.encode("utf-8")
try:
userdata = userdata.encode("utf-8")
except AttributeError:
# In python 3, 'bytes' object has no attribute 'encode'
pass
else:
userdata = encodeutils.safe_encode(userdata)
try:
userdata = encodeutils.safe_encode(userdata)
except UnicodeDecodeError:
pass
userdata_b64 = base64.b64encode(userdata).decode('utf-8')
body["server"]["user_data"] = userdata_b64