Merge "Add support for the httpsRedirect property"

This commit is contained in:
Jenkins 2015-04-16 16:39:13 +00:00 committed by Gerrit Code Review
commit 484feb9814
2 changed files with 140 additions and 2 deletions

View File

@ -26,6 +26,7 @@ from heat.engine import function
from heat.engine import properties
from heat.engine import resource
from heat.engine import scheduler
from heat.engine import support
try:
from pyrax.exceptions import NotFound # noqa
@ -78,14 +79,17 @@ class CloudLoadBalancer(resource.Resource):
CONNECTION_LOGGING, METADATA, PORT, TIMEOUT,
CONNECTION_THROTTLE, SESSION_PERSISTENCE, VIRTUAL_IPS,
CONTENT_CACHING, HEALTH_MONITOR, SSL_TERMINATION, ERROR_PAGE,
HTTPS_REDIRECT,
) = (
'name', 'nodes', 'protocol', 'accessList', 'halfClosed', 'algorithm',
'connectionLogging', 'metadata', 'port', 'timeout',
'connectionThrottle', 'sessionPersistence', 'virtualIps',
'contentCaching', 'healthMonitor', 'sslTermination', 'errorPage',
'httpsRedirect',
)
LB_UPDATE_PROPS = (NAME, ALGORITHM, PROTOCOL, HALF_CLOSED, PORT, TIMEOUT)
LB_UPDATE_PROPS = (NAME, ALGORITHM, PROTOCOL, HALF_CLOSED, PORT, TIMEOUT,
HTTPS_REDIRECT)
_NODE_KEYS = (
NODE_ADDRESSES, NODE_PORT, NODE_CONDITION, NODE_TYPE,
@ -428,6 +432,19 @@ class CloudLoadBalancer(resource.Resource):
properties.Schema.STRING,
update_allowed=True
),
HTTPS_REDIRECT: properties.Schema(
properties.Schema.BOOLEAN,
_("Enables or disables HTTP to HTTPS redirection for the load "
"balancer. When enabled, any HTTP request returns status code "
"301 (Moved Permanently), and the requester is redirected to "
"the requested URL via the HTTPS protocol on port 443. Only "
"available for HTTPS protocol (port=443), or HTTP protocol with "
"a properly configured SSL termination (secureTrafficOnly=true, "
"securePort=443)."),
update_allowed=True,
default=False,
support_status=support.SupportStatus(version="2015.1")
)
}
attributes_schema = {
@ -557,6 +574,7 @@ class CloudLoadBalancer(resource.Resource):
'sessionPersistence': session_persistence,
'timeout': self.properties.get(self.TIMEOUT),
'connectionLogging': connection_logging,
self.HTTPS_REDIRECT: self.properties[self.HTTPS_REDIRECT]
}
lb_name = (self.properties.get(self.NAME) or
@ -886,6 +904,22 @@ class CloudLoadBalancer(resource.Resource):
function.resolve,
self.name).validate()
# validate if HTTPS_REDIRECT is true and we're not HTTPS
redir = self.properties[self.HTTPS_REDIRECT]
proto = self.properties[self.PROTOCOL]
if redir and (proto != "HTTPS"):
termcfg = self.properties.get(self.SSL_TERMINATION) or {}
seconly = termcfg.get(self.SSL_TERMINATION_SECURE_TRAFFIC_ONLY,
False)
secport = termcfg.get(self.SSL_TERMINATION_SECURE_PORT, 0)
if not (seconly and (secport == 443) and (proto == "HTTP")):
message = _("HTTPS redirect is only available for the HTTPS "
"protocol (port=443), or the HTTP protocol with "
"a properly configured SSL termination "
"(secureTrafficOnly=true, securePort=443).")
raise exception.StackValidationFailed(message=message)
# if a vip specifies and id, it can't specify version or type;
# otherwise version and type are required
for vip in self.properties.get(self.VIRTUAL_IPS, []):

View File

@ -325,7 +325,9 @@ class LoadBalancerTest(common.HeatTestCase):
"healthMonitor": None,
"metadata": None,
"sessionPersistence": None,
"timeout": 110
"timeout": 110,
"httpsRedirect": False
}
lb.resource_mapping = override_resource
@ -910,6 +912,30 @@ class LoadBalancerTest(common.HeatTestCase):
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
self.m.VerifyAll()
def test_update_lb_redirect(self):
templ = copy.deepcopy(self.lb_template)
rsrs = templ['Resources'].values()[0]
rsrs['Properties']['protocol'] = "HTTPS"
rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
self.lb_name,
self.expected_body)
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
update_template = copy.deepcopy(rsrc.t)
update_template['Properties']['httpsRedirect'] = True
self.m.StubOutWithMock(rsrc.clb, 'get')
rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
self.m.StubOutWithMock(fake_loadbalancer, 'update')
fake_loadbalancer.update(httpsRedirect=True)
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.update, update_template)()
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
self.m.VerifyAll()
def test_update_lb_half_closed(self):
rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
self.lb_name,
@ -1497,3 +1523,81 @@ class LoadBalancerTest(common.HeatTestCase):
res = mock_loadbalancer.check_delete_complete(mock_task)
self.assertTrue(res)
def test_redir(self):
mock_stack = mock.Mock()
mock_stack.db_resource_get.return_value = None
props = {'httpsRedirect': True,
'protocol': 'HTTPS',
'port': 443,
'nodes': [],
'virtualIps': [{'id': '1234'}]}
mock_resdef = rsrc_defn.ResourceDefinition("test_lb",
LoadBalancerWithFakeClient,
properties=props)
mock_lb = lb.CloudLoadBalancer("test", mock_resdef, mock_stack)
self.assertIsNone(mock_lb.validate())
props['protocol'] = 'HTTP'
props['sslTermination'] = {
'secureTrafficOnly': True,
'securePort': 443,
'privatekey': "bobloblaw",
'certificate': 'mycert'
}
mock_resdef = rsrc_defn.ResourceDefinition("test_lb_2",
LoadBalancerWithFakeClient,
properties=props)
mock_lb = lb.CloudLoadBalancer("test_2", mock_resdef, mock_stack)
self.assertIsNone(mock_lb.validate())
def test_invalid_redir_proto(self):
mock_stack = mock.Mock()
mock_stack.db_resource_get.return_value = None
props = {'httpsRedirect': True,
'protocol': 'TCP',
'port': 1234,
'nodes': [],
'virtualIps': [{'id': '1234'}]}
mock_resdef = rsrc_defn.ResourceDefinition("test_lb",
LoadBalancerWithFakeClient,
properties=props)
mock_lb = lb.CloudLoadBalancer("test", mock_resdef, mock_stack)
ex = self.assertRaises(exception.StackValidationFailed,
mock_lb.validate)
self.assertIn("HTTPS redirect is only available", six.text_type(ex))
def test_invalid_redir_ssl(self):
mock_stack = mock.Mock()
mock_stack.db_resource_get.return_value = None
props = {'httpsRedirect': True,
'protocol': 'HTTP',
'port': 1234,
'nodes': [],
'virtualIps': [{'id': '1234'}]}
mock_resdef = rsrc_defn.ResourceDefinition("test_lb",
LoadBalancerWithFakeClient,
properties=props)
mock_lb = lb.CloudLoadBalancer("test", mock_resdef, mock_stack)
ex = self.assertRaises(exception.StackValidationFailed,
mock_lb.validate)
self.assertIn("HTTPS redirect is only available", six.text_type(ex))
props['sslTermination'] = {
'secureTrafficOnly': False,
'securePort': 443,
'privatekey': "bobloblaw",
'certificate': 'mycert'
}
mock_lb = lb.CloudLoadBalancer("test", mock_resdef, mock_stack)
ex = self.assertRaises(exception.StackValidationFailed,
mock_lb.validate)
self.assertIn("HTTPS redirect is only available", six.text_type(ex))
props['sslTermination'] = {
'secureTrafficOnly': True,
'securePort': 1234,
'privatekey': "bobloblaw",
'certificate': 'mycert'
}
mock_lb = lb.CloudLoadBalancer("test", mock_resdef, mock_stack)
ex = self.assertRaises(exception.StackValidationFailed,
mock_lb.validate)
self.assertIn("HTTPS redirect is only available", six.text_type(ex))