Encode/Decode data when copying files
This is the client side change of server patch [1]. If files are copied to container, the file data will be encoded at client side and decoded at server side. If files are copied from container, the file data will be encoded at server side and decoded at client side. [1] https://review.openstack.org/#/c/599202/ Depends-On: https://review.openstack.org/#/c/599202/ Change-Id: Id9fe25e06052d46dcc33a1e05100ef7a2d7db018 Closes-Bug: #1789777
This commit is contained in:
@@ -31,7 +31,7 @@ if not LOG.handlers:
|
||||
HEADER_NAME = "OpenStack-API-Version"
|
||||
SERVICE_TYPE = "container"
|
||||
MIN_API_VERSION = '1.1'
|
||||
MAX_API_VERSION = '1.24'
|
||||
MAX_API_VERSION = '1.25'
|
||||
DEFAULT_API_VERSION = MAX_API_VERSION
|
||||
|
||||
_SUBSTITUTIONS = {}
|
||||
|
@@ -14,11 +14,14 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
from oslo_utils import netutils
|
||||
import six
|
||||
from six.moves.urllib import parse
|
||||
from six.moves.urllib import request
|
||||
from zunclient.common.apiclient import exceptions as apiexec
|
||||
@@ -364,3 +367,17 @@ def list_container_networks(networks):
|
||||
utils.print_list(networks, columns,
|
||||
{'fixed_ips': format_network_fixed_ips},
|
||||
sortby_index=None)
|
||||
|
||||
|
||||
def encode_file_data(data):
|
||||
if six.PY3 and isinstance(data, str):
|
||||
data = data.encode('utf-8')
|
||||
return base64.b64encode(data).decode('utf-8')
|
||||
|
||||
|
||||
def decode_file_data(data):
|
||||
# Py3 raises binascii.Error instead of TypeError as in Py27
|
||||
try:
|
||||
return base64.b64decode(data)
|
||||
except (TypeError, binascii.Error):
|
||||
raise exc.CommandError(_('Invalid Base 64 file data.'))
|
||||
|
@@ -1068,7 +1068,7 @@ class CopyContainer(command.Command):
|
||||
|
||||
res = client.containers.get_archive(**opts)
|
||||
dest_path = parsed_args.destination
|
||||
tardata = io.BytesIO(res['data'].encode())
|
||||
tardata = io.BytesIO(res['data'])
|
||||
with closing(tarfile.open(fileobj=tardata)) as tar:
|
||||
tar.extractall(dest_path)
|
||||
|
||||
|
@@ -246,7 +246,7 @@ class ShellTest(utils.TestCase):
|
||||
project_domain_id='', project_domain_name='',
|
||||
user_domain_id='', user_domain_name='', profile=None,
|
||||
endpoint_override=None, insecure=False, cacert=None,
|
||||
version=api_versions.APIVersion('1.24'))
|
||||
version=api_versions.APIVersion('1.25'))
|
||||
|
||||
def test_main_option_region(self):
|
||||
self.make_env()
|
||||
@@ -274,7 +274,7 @@ class ShellTest(utils.TestCase):
|
||||
project_domain_id='', project_domain_name='',
|
||||
user_domain_id='', user_domain_name='', profile=None,
|
||||
endpoint_override=None, insecure=False, cacert=None,
|
||||
version=api_versions.APIVersion('1.24'))
|
||||
version=api_versions.APIVersion('1.25'))
|
||||
|
||||
@mock.patch('zunclient.client.Client')
|
||||
def test_main_endpoint_internal(self, mock_client):
|
||||
@@ -288,7 +288,7 @@ class ShellTest(utils.TestCase):
|
||||
project_domain_id='', project_domain_name='',
|
||||
user_domain_id='', user_domain_name='', profile=None,
|
||||
endpoint_override=None, insecure=False, cacert=None,
|
||||
version=api_versions.APIVersion('1.24'))
|
||||
version=api_versions.APIVersion('1.25'))
|
||||
|
||||
|
||||
class ShellTestKeystoneV3(ShellTest):
|
||||
@@ -320,4 +320,4 @@ class ShellTestKeystoneV3(ShellTest):
|
||||
user_domain_id='', user_domain_name='Default',
|
||||
endpoint_override=None, insecure=False, profile=None,
|
||||
cacert=None,
|
||||
version=api_versions.APIVersion('1.24'))
|
||||
version=api_versions.APIVersion('1.25'))
|
||||
|
@@ -14,6 +14,7 @@ import copy
|
||||
from six.moves.urllib import parse
|
||||
import testtools
|
||||
from testtools import matchers
|
||||
from zunclient.common import utils as zun_utils
|
||||
from zunclient import exceptions
|
||||
from zunclient.tests.unit import utils
|
||||
from zunclient.v1 import containers
|
||||
@@ -289,7 +290,7 @@ fake_responses = {
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
None,
|
||||
{'data': data},
|
||||
),
|
||||
},
|
||||
'/v1/containers/%s/put_archive?%s'
|
||||
@@ -297,7 +298,7 @@ fake_responses = {
|
||||
{
|
||||
'POST': (
|
||||
{},
|
||||
{'data': data},
|
||||
None,
|
||||
),
|
||||
},
|
||||
'/v1/containers/%s/stats?%s'
|
||||
@@ -653,25 +654,25 @@ class ContainerManagerTest(testtools.TestCase):
|
||||
self.assertIsNone(containers)
|
||||
|
||||
def test_containers_get_archive(self):
|
||||
containers = self.mgr.get_archive(CONTAINER1['id'], path)
|
||||
response = self.mgr.get_archive(CONTAINER1['id'], path)
|
||||
expect = [
|
||||
('GET', '/v1/containers/%s/get_archive?%s'
|
||||
% (CONTAINER1['id'], parse.urlencode({'path': path})),
|
||||
{'Content-Length': '0'}, None)
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertIsNone(containers)
|
||||
self.assertEqual(zun_utils.decode_file_data(data), response['data'])
|
||||
|
||||
def test_containers_put_archive(self):
|
||||
containers = self.mgr.put_archive(CONTAINER1['id'], path, data)
|
||||
response = self.mgr.put_archive(CONTAINER1['id'], path, data)
|
||||
expect = [
|
||||
('POST', '/v1/containers/%s/put_archive?%s'
|
||||
% (CONTAINER1['id'], parse.urlencode({'path': path})),
|
||||
{'Content-Length': '0'},
|
||||
{'data': data})
|
||||
{'data': zun_utils.encode_file_data(data)})
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertTrue(containers)
|
||||
self.assertTrue(response)
|
||||
|
||||
def test_containers_commit(self):
|
||||
containers = self.mgr.commit(CONTAINER1['id'], repo, tag)
|
||||
|
@@ -196,10 +196,19 @@ class ContainerManager(base.Manager):
|
||||
qparams={'ps_args': ps_args})[1]
|
||||
|
||||
def get_archive(self, id, path):
|
||||
return self._action(id, '/get_archive', method='GET',
|
||||
qparams={'path': path})[1]
|
||||
res = self._action(id, '/get_archive', method='GET',
|
||||
qparams={'path': path})[1]
|
||||
# API version 1.25 or later will return Base64-encoded data
|
||||
if self.api_version >= api_versions.APIVersion("1.25"):
|
||||
res['data'] = utils.decode_file_data(res['data'])
|
||||
else:
|
||||
res['data'] = res['data'].encode()
|
||||
return res
|
||||
|
||||
def put_archive(self, id, path, data):
|
||||
# API version 1.25 or later will expect Base64-encoded data
|
||||
if self.api_version >= api_versions.APIVersion("1.25"):
|
||||
data = utils.encode_file_data(data)
|
||||
return self._action(id, '/put_archive',
|
||||
qparams={'path': path},
|
||||
body={'data': data})
|
||||
|
@@ -865,7 +865,7 @@ def do_cp(cs, args):
|
||||
|
||||
res = cs.containers.get_archive(**opts)
|
||||
dest_path = args.destination
|
||||
tardata = io.BytesIO(res['data'].encode())
|
||||
tardata = io.BytesIO(res['data'])
|
||||
with closing(tarfile.open(fileobj=tardata)) as tar:
|
||||
tar.extractall(dest_path)
|
||||
|
||||
|
Reference in New Issue
Block a user