diff --git a/heat/engine/constraint/common_constraints.py b/heat/engine/constraint/common_constraints.py index 658e6b736b..3d3637587a 100644 --- a/heat/engine/constraint/common_constraints.py +++ b/heat/engine/constraint/common_constraints.py @@ -153,3 +153,21 @@ class TimezoneConstraint(constraints.BaseCustomConstraint): self._error_message = _( 'Invalid timezone: %s') % six.text_type(ex) return False + + +class ExpirationConstraint(constraints.BaseCustomConstraint): + + def validate(self, value, context): + if not value: + return True + try: + expiration_tz = timeutils.parse_isotime(value.strip()) + expiration = timeutils.normalize_time(expiration_tz) + if expiration > timeutils.utcnow(): + return True + raise ValueError(_('Expiration time is out of date.')) + except Exception as ex: + self._error_message = (_( + 'Expiration {0} is invalid: {1}').format(value, + six.text_type(ex))) + return False diff --git a/heat/engine/resources/openstack/barbican/order.py b/heat/engine/resources/openstack/barbican/order.py index 5fb584e4b6..c2a068316e 100644 --- a/heat/engine/resources/openstack/barbican/order.py +++ b/heat/engine/resources/openstack/barbican/order.py @@ -92,7 +92,7 @@ class Order(resource.Resource): properties.Schema.STRING, _('The expiration date for the secret in ISO-8601 format.'), constraints=[ - constraints.CustomConstraint('iso_8601'), + constraints.CustomConstraint('expiration'), ], ), ALGORITHM: properties.Schema( diff --git a/heat/engine/resources/openstack/barbican/secret.py b/heat/engine/resources/openstack/barbican/secret.py index 77c009e0fe..c7dabfd8fc 100644 --- a/heat/engine/resources/openstack/barbican/secret.py +++ b/heat/engine/resources/openstack/barbican/secret.py @@ -98,7 +98,7 @@ class Secret(resource.Resource): properties.Schema.STRING, _('The expiration date for the secret in ISO-8601 format.'), constraints=[ - constraints.CustomConstraint('iso_8601'), + constraints.CustomConstraint('expiration'), ], ), ALGORITHM: properties.Schema( diff --git a/heat/tests/constraints/test_common_constraints.py b/heat/tests/constraints/test_common_constraints.py index 18ddaf4333..eadd317b2e 100644 --- a/heat/tests/constraints/test_common_constraints.py +++ b/heat/tests/constraints/test_common_constraints.py @@ -305,3 +305,40 @@ class FIPDNSNameConstraintTest(common.HeatTestCase): def test_validation_none(self): self.assertTrue(self.constraint.validate(None, self.ctx)) + + +class ExpirationConstraintTest(common.HeatTestCase): + + def setUp(self): + super(ExpirationConstraintTest, self).setUp() + self.ctx = utils.dummy_context() + self.constraint = cc.ExpirationConstraint() + + def test_validate_date_format(self): + date = '2050-01-01' + self.assertTrue(self.constraint.validate(date, None)) + + def test_validation_error(self): + expiration = "Fri 13th, 2050" + expected = ("Expiration {0} is invalid: Unable to parse " + "date string '{0}'".format(expiration)) + + self.assertFalse(self.constraint.validate(expiration, self.ctx)) + self.assertEqual( + expected, + six.text_type(self.constraint._error_message) + ) + + def test_validation_before_current_time(self): + expiration = "1970-01-01" + expected = ("Expiration %s is invalid: Expiration time " + "is out of date." % expiration) + + self.assertFalse(self.constraint.validate(expiration, self.ctx)) + self.assertEqual( + expected, + six.text_type(self.constraint._error_message) + ) + + def test_validation_none(self): + self.assertTrue(self.constraint.validate(None, self.ctx)) diff --git a/setup.cfg b/setup.cfg index 5cc8e69393..faa6cec909 100644 --- a/setup.cfg +++ b/setup.cfg @@ -83,6 +83,7 @@ heat.constraints = cron_expression = heat.engine.constraint.common_constraints:CRONExpressionConstraint dns_domain = heat.engine.constraint.common_constraints:DNSDomainConstraint dns_name = heat.engine.constraint.common_constraints:DNSNameConstraint + expiration = heat.engine.constraint.common_constraints:ExpirationConstraint rel_dns_name = heat.engine.constraint.common_constraints:RelativeDNSNameConstraint ip_addr = heat.engine.constraint.common_constraints:IPConstraint iso_8601 = heat.engine.constraint.common_constraints:ISO8601Constraint