Skip to content

Commit

Permalink
Merge branch '2.7-testing' into 2.7-dev
Browse files Browse the repository at this point in the history
Conflicts:
	bindings/pulp/bindings/bindings.py
  • Loading branch information
asmacdo committed Sep 17, 2015
2 parents 74b3957 + af30f5a commit 5e8fb60
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 54 deletions.
2 changes: 2 additions & 0 deletions bindings/pulp/bindings/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pulp.bindings.consumer_groups import *
from pulp.bindings.consumer import *
from pulp.bindings.server_info import ServerInfoAPI, ServerStatusAPI
from pulp.bindings.static import StaticRequest
from pulp.bindings.tasks import TasksAPI, TaskSearchAPI
from pulp.bindings.upload import UploadAPI
from pulp.bindings.auth import *
Expand Down Expand Up @@ -55,6 +56,7 @@ def __init__(self, pulp_connection):
self.role = RoleAPI(pulp_connection)
self.server_info = ServerInfoAPI(pulp_connection)
self.server_status = ServerStatusAPI(pulp_connection)
self.static = StaticRequest(pulp_connection)
self.tasks = TasksAPI(pulp_connection)
self.tasks_search = TaskSearchAPI(pulp_connection)
self.uploads = UploadAPI(pulp_connection)
Expand Down
35 changes: 21 additions & 14 deletions bindings/pulp/bindings/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,27 +84,29 @@ def __init__(self,
self.verify_ssl = verify_ssl
self.ca_path = ca_path

def DELETE(self, path, body=None, log_request_body=True):
return self._request('DELETE', path, body=body, log_request_body=log_request_body)
def DELETE(self, path, body=None, log_request_body=True, ignore_prefix=False):
return self._request('DELETE', path, body=body, log_request_body=log_request_body,
ignore_prefix=ignore_prefix)

def GET(self, path, queries=()):
return self._request('GET', path, queries)
def GET(self, path, queries=(), ignore_prefix=False):
return self._request('GET', path, queries, ignore_prefix=ignore_prefix)

def HEAD(self, path):
return self._request('HEAD', path)
def HEAD(self, path, ignore_prefix=False):
return self._request('HEAD', path, ignore_prefix=ignore_prefix)

def POST(self, path, body=None, ensure_encoding=True, log_request_body=True):
def POST(self, path, body=None, ensure_encoding=True, log_request_body=True,
ignore_prefix=False):
return self._request('POST', path, body=body, ensure_encoding=ensure_encoding,
log_request_body=log_request_body)
log_request_body=log_request_body, ignore_prefix=ignore_prefix)

def PUT(self, path, body, ensure_encoding=True, log_request_body=True):
def PUT(self, path, body, ensure_encoding=True, log_request_body=True, ignore_prefix=False):
return self._request('PUT', path, body=body, ensure_encoding=ensure_encoding,
log_request_body=log_request_body)
log_request_body=log_request_body, ignore_prefix=ignore_prefix)

# protected request utilities ---------------------------------------------

def _request(self, method, path, queries=(), body=None, ensure_encoding=True,
log_request_body=True):
log_request_body=True, ignore_prefix=False):
"""
make a HTTP request to the pulp server and return the response
Expand All @@ -130,14 +132,17 @@ def _request(self, method, path, queries=(), body=None, ensure_encoding=True,
:param log_request_body: Toggle logging of the request body, defaults to true
:type log_request_body: bool
:param ignore_prefix: when building the url, disregard the self.path_prefix
:type ignore_prefix: bool
:return: Response object
:rtype: pulp.bindings.responses.Response
:raises: ConnectionException or one of the RequestExceptions
(depending on response codes) in case of unsuccessful
request
"""
url = self._build_url(path, queries)
url = self._build_url(path, queries, ignore_prefix)
if ensure_encoding:
body = self._process_body(body)
if not isinstance(body, (NoneType, basestring)):
Expand Down Expand Up @@ -201,7 +206,7 @@ def _handle_exceptions(self, response_code, response_body):
else:
raise code_class_mappings[response_code](response_body)

def _build_url(self, path, queries=()):
def _build_url(self, path, queries, ignore_prefix):
"""
Takes a relative path and query parameters, combines them with the
base path, and returns the result. Handles utf-8 encoding as necessary.
Expand All @@ -217,13 +222,15 @@ def _build_url(self, path, queries=()):
in either case representing key-value pairs to be used
as query parameters on the URL.
:type queries: mapping object or sequence of 2-element tuples
:param ignore_prefix: when building the url, disregard the self.path_prefix
:type ignore_prefix: bool
:return: path that is a composite of self.path_prefix, path, and
queries. May be relative or absolute depending on the nature
of self.path_prefix
"""
# build the request url from the path and queries dict or tuple
if not path.startswith(self.path_prefix):
if not path.startswith(self.path_prefix) and not ignore_prefix:
if path.startswith('/'):
path = path[1:]
path = '/'.join((self.path_prefix, path))
Expand Down
16 changes: 16 additions & 0 deletions bindings/pulp/bindings/static.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pulp.bindings.base import PulpAPI


class StaticRequest(PulpAPI):
"""
Connection class to access static calls
"""

def get_server_key(self):
"""
Retrieve the server's public key.
:return: rsa public key
:rtype: str
"""
return self.server.GET('/pulp/static/rsa_pub.key', ignore_prefix=True)
22 changes: 22 additions & 0 deletions bindings/test/unit/test_static.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import unittest

import mock

from pulp.bindings.server import PulpConnection
from pulp.bindings.static import StaticRequest


class TestStaticRequest(unittest.TestCase):
"""
Tests for static file requests.
"""

def test_get_server_key(self):
"""
Test that the correct path is given to the binding.
"""
static_request = StaticRequest(mock.MagicMock(spec=PulpConnection))
response = static_request.get_server_key()
static_request.server.GET.assert_called_once_with('/pulp/static/rsa_pub.key',
ignore_prefix=True)
self.assertTrue(response is static_request.server.GET.return_value)
68 changes: 28 additions & 40 deletions client_consumer/pulp/client/consumer/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,43 +91,42 @@ def initialize(context):
context.cli.add_command(StatusCommand(context, 'status', _(d)))


def download(url, location):
def write_to_location(location, content):
"""
Download files to the specified location.
:param url: The file URL.
:type url: str
:param location: The absolute path to where the downloaded
file is to be stored.
:type location: str
Write content to a path. Ensures that the entire path exists, creating directories if necessary.
:param location: path that should exist
:type location: str
:param content: bits to be written to file
:type content: str
"""
request = urllib2.urlopen(url)
try:
content = request.read()
os.makedirs(os.path.dirname(location))
except OSError, e:
if e.errno != errno.EEXIST:
raise
try:
fp = open(location, 'w+')
try:
fp.write(content)
finally:
fp.close()
fp.write(content)
finally:
request.close()
fp.close()


def update_server_key(conf):
def update_server_key(command_inst):
"""
Download the server's RSA key and store in the location
specified in the configuration.
:param conf: The consumer configuration object.
:type conf: dict
Ensure that the server's public key stored on the consumer is up to date.
:param command_inst: instance of a CLI command
:type command_inst: pulp.client.extensions.extensions.PulpCliCommand
"""
host = conf['server']['host']
location = conf['server']['rsa_pub']
url = 'https://%s/pulp/static/rsa_pub.key' % host
try:
os.makedirs(os.path.dirname(location))
except OSError, e:
if e.errno != errno.EEXIST:
raise
download(url, location)
key_reply = command_inst.context.server.static.get_server_key()
except Exception, e:
msg = _('Download server RSA key failed [%(e)s]' % {'e': e})
command_inst.prompt.render_failure_message(msg)
else:
key_location = command_inst.context.config['server']['rsa_pub']
write_to_location(key_location, key_reply.response_body)


# -- common exceptions --------------------------------------------------------
Expand Down Expand Up @@ -201,14 +200,7 @@ def register(self, **kwargs):
finally:
fp.close()

# download server public key

try:
update_server_key(self.context.config)
except Exception, e:
msg = _('Download server RSA key failed [%(e)s]' % {'e': e})
self.prompt.render_failure_message(msg)

update_server_key(self)
self.prompt.render_success_message('Consumer [%s] successfully registered' % consumer_id)


Expand Down Expand Up @@ -250,11 +242,7 @@ def update(self, **kwargs):
self.prompt.render_success_message('Consumer [%s] successfully updated' % consumer_id)
if not kwargs.get(OPTION_EXCHANGE_KEYS.keyword):
return
try:
update_server_key(self.context.config)
except Exception, e:
msg = _('Download server RSA key failed [%(e)s]' % {'e': e})
self.prompt.render_failure_message(msg)
update_server_key(self)
except NotFoundException:
self.prompt.write('Consumer [%s] does not exist on the server' % consumer_id, tag='not-found')

Expand Down
116 changes: 116 additions & 0 deletions client_consumer/test/unit/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import unittest

import mock

from pulp.client.consumer import cli


class TestWriteToLocation(unittest.TestCase):
"""
Tests for writing content to a file.
"""

@mock.patch('pulp.client.consumer.cli.os')
@mock.patch('__builtin__.open', new_callable=mock.MagicMock())
def test_dir_struct_exists(self, mopen, mock_os):
"""
Test that when the directory structure already exists, the write still happens.
"""

class MockException(OSError):
pass

mock_e = MockException()
mock_e.errno = 17
mock_os.makedirs.side_effect = mock_e
mock_fp = open.return_value

cli.write_to_location('test/loc', 'content')
mock_os.path.dirname.assert_called_once_with('test/loc')
mock_os.makedirs.assert_called_once_with(mock_os.path.dirname.return_value)
open.assert_called_once_with('test/loc', 'w+')
mock_fp.write.assert_called_once_with('content')
mock_fp.close.assert_called_once_with()

@mock.patch('pulp.client.consumer.cli.os')
@mock.patch('__builtin__.open', new_callable=mock.MagicMock())
def test_misc_os_err(self, mopen, mock_os):
"""
Test that misc errors are reraised and the write does not happen.
"""

class MockException(OSError):
pass

mock_e = MockException()
mock_e.errno = 16
mock_os.makedirs.side_effect = mock_e
mock_fp = open.return_value

self.assertRaises(MockException, cli.write_to_location, 'test/loc', 'content')
self.assertEqual(mock_fp.write.call_count, 0)

@mock.patch('pulp.client.consumer.cli.os')
@mock.patch('__builtin__.open', new_callable=mock.MagicMock())
def test_write_err(self, *_):
"""
If there is a problem with the write, the file is still closed.
"""

class MockException(Exception):
pass

mock_fp = open.return_value
mock_fp.write.side_effect = MockException
self.assertRaises(MockException, cli.write_to_location, 'test/loc', 'content')
mock_fp.write.assert_called_once_with('content')
mock_fp.close.assert_called_once_with()

@mock.patch('pulp.client.consumer.cli.os')
@mock.patch('__builtin__.open', new_callable=mock.MagicMock())
def test_as_expected(self, mopen, mock_os):
"""
When everything works as expected, ensure that the file is closed.
"""
mock_fp = open.return_value
cli.write_to_location('test/loc', 'content')
mock_os.path.dirname.assert_called_once_with('test/loc')
mock_os.makedirs.assert_called_once_with(mock_os.path.dirname.return_value)
open.assert_called_once_with('test/loc', 'w+')
mock_fp.write.assert_called_once_with('content')
mock_fp.close.assert_called_once_with()


class TestUpdateServerKey(unittest.TestCase):
"""
Tests for updating the server key.
"""

@mock.patch('pulp.client.consumer.cli.write_to_location')
def test_as_expected(self, mock_write):
"""
Everything is as expected, content is written to the location in the config file.
"""
mock_cmd = mock.MagicMock()
key_response = mock_cmd.context.server.static.get_server_key.return_value
key_loc = mock_cmd.context.config['getter']['getter']
cli.update_server_key(mock_cmd)
mock_write.assert_called_once_with(key_loc, key_response.response_body)

@mock.patch('pulp.client.consumer.cli.write_to_location')
def test_binding_exception(self, mock_write):
"""
If there is a problem getting the key, do not attempt to write the file.
"""

class MockException(Exception):

def __str__(self):
return "Mock Exception str"

mock_cmd = mock.MagicMock()
mock_cmd.context.server.static.get_server_key.side_effect = MockException()
cli.update_server_key(mock_cmd)
msg = 'Download server RSA key failed [Mock Exception str]'
mock_cmd.prompt.render_failure_message.assert_called_once_with(msg)
self.assertEqual(mock_write.call_count, 0)

0 comments on commit 5e8fb60

Please sign in to comment.