Remove support for accessing ResourceDefinition like a dict

Heat no longer accesses resource definitions as if they were snippets of a
CloudFormation template, but rather uses the APIs of the ResourceDefinition
class. The ability to treat this class as a snippet of CloudFormation
template has been deprecated for several release cycles to allow
third-party plugins to move away from it; this change removes that ability.

Change-Id: Ia9c63662438d4c979b897118b3a7a736161bcbca
This commit is contained in:
Zane Bitter 2016-04-13 19:49:05 -04:00
parent 53482b0d37
commit 2c6fc7bcd6
2 changed files with 9 additions and 167 deletions

View File

@ -14,7 +14,6 @@ import collections
import copy
import itertools
import operator
import warnings
import six
@ -28,15 +27,15 @@ __all__ = ['ResourceDefinition']
@repr_wrapper
class ResourceDefinitionCore(object):
class ResourceDefinition(object):
"""A definition of a resource, independent of any template format."""
class Diff(object):
"""A diff between two versions of the same resource definition."""
def __init__(self, old_defn, new_defn):
if not (isinstance(old_defn, ResourceDefinitionCore) and
isinstance(new_defn, ResourceDefinitionCore)):
if not (isinstance(old_defn, ResourceDefinition) and
isinstance(new_defn, ResourceDefinition)):
raise TypeError
self.old_defn = old_defn
@ -218,8 +217,8 @@ class ResourceDefinitionCore(object):
function.dependencies(data, datapath))
explicit_depends = [] if self._depends is None else self._depends
prop_deps = strict_func_deps(self._properties, path(PROPERTIES))
metadata_deps = strict_func_deps(self._metadata, path(METADATA))
prop_deps = strict_func_deps(self._properties, path('Properties'))
metadata_deps = strict_func_deps(self._metadata, path('Metadata'))
# (ricolin) External resource should not depend on any other resources.
# This operation is not allowed for now.
@ -242,7 +241,7 @@ class ResourceDefinitionCore(object):
"""
return properties.Properties(schema, self._properties or {},
function.resolve, self.name, context,
section=PROPERTIES)
section='Properties')
def deletion_policy(self):
"""Return the deletion policy for the resource.
@ -259,7 +258,7 @@ class ResourceDefinitionCore(object):
"""
return properties.Properties(schema, self._update_policy or {},
function.resolve, self.name, context,
section=UPDATE_POLICY)
section='UpdatePolicy')
def metadata(self):
"""Return the resource metadata."""
@ -307,7 +306,7 @@ class ResourceDefinitionCore(object):
Return a Diff object that can be used to establish differences between
this definition and a previous definition of the same resource.
"""
if not isinstance(previous, ResourceDefinitionCore):
if not isinstance(previous, ResourceDefinition):
return NotImplemented
return self.Diff(previous, self)
@ -320,7 +319,7 @@ class ResourceDefinitionCore(object):
ignored, as are the actual values that any included functions resolve
to.
"""
if not isinstance(other, ResourceDefinitionCore):
if not isinstance(other, ResourceDefinition):
return NotImplemented
return self.render_hot() == other.render_hot()
@ -362,120 +361,6 @@ class ResourceDefinitionCore(object):
return '%(classname)s(%(name)s, %(type)s, %(args)s)' % data
_KEYS = (
TYPE, PROPERTIES, METADATA, DELETION_POLICY, UPDATE_POLICY,
DEPENDS_ON, DESCRIPTION,
) = (
'Type', 'Properties', 'Metadata', 'DeletionPolicy', 'UpdatePolicy',
'DependsOn', 'Description',
)
class ResourceDefinition(ResourceDefinitionCore, collections.Mapping):
"""A resource definition that also acts like a cfn template snippet.
This class exists only for backwards compatibility with existing resource
plugins and unit tests; it is deprecated and will be replaced with
ResourceDefinitionCore, possibly as soon as the Ocata release.
"""
_deprecation_msg = (
'Reading the ResourceDefinition as if it were a snippet of a '
'CloudFormation template is deprecated, and the ability to treat it '
'as such will be removed in the future. Resource plugins should use '
'the ResourceDefinition API to work with the definition of the '
'resource instance.')
def __eq__(self, other):
"""Compare this resource definition for equality with another.
Two resource definitions are considered to be equal if they can be
generated from the same template snippet. The name of the resource is
ignored, as are the actual values that any included functions resolve
to.
This method can also compare the resource definition to a template
snippet. In this case, two snippets are considered equal if they
compare equal in a dictionary comparison. (Specifically, this means
that intrinsic functions are compared by their results.) This exists
solely to not break existing unit tests.
"""
if not isinstance(other, ResourceDefinitionCore):
if isinstance(other, collections.Mapping):
return dict(self) == other
return super(ResourceDefinition, self).__eq__(other)
__hash__ = ResourceDefinitionCore.__hash__
def __iter__(self):
"""Iterate over the available CFN template keys.
This is for backwards compatibility with existing code that expects a
parsed-JSON template snippet.
"""
warnings.warn(self._deprecation_msg, DeprecationWarning)
yield TYPE
if self._properties is not None:
yield PROPERTIES
if self._metadata is not None:
yield METADATA
if self._deletion_policy is not None:
yield DELETION_POLICY
if self._update_policy is not None:
yield UPDATE_POLICY
if self._depends:
yield DEPENDS_ON
if self.description:
yield DESCRIPTION
def __getitem__(self, key):
"""Get the specified item from a CFN template snippet.
This is for backwards compatibility with existing code that expects a
parsed-JSON template snippet.
"""
warnings.warn(self._deprecation_msg, DeprecationWarning)
if key == TYPE:
return self.resource_type
elif key == PROPERTIES:
if self._properties is not None:
return self._properties
elif key == METADATA:
if self._metadata is not None:
return self._metadata
elif key == DELETION_POLICY:
if self._deletion_policy is not None:
return self._deletion_policy
elif key == UPDATE_POLICY:
if self._update_policy is not None:
return self._update_policy
elif key == DEPENDS_ON:
if self._depends:
if len(self._depends) == 1:
return self._depends[0]
return self._depends
elif key == DESCRIPTION:
if self.description:
return self.description
raise KeyError(key)
def __len__(self):
"""Return the number of available CFN template keys.
This is for backwards compatibility with existing code that expects a
parsed-JSON template snippet.
"""
return len(list(iter(self)))
def __repr__(self):
"""Return a string representation of the resource definition."""
return 'ResourceDefinition %s' % repr(dict(self))
def _hash_data(data):
"""Return a stable hash value for an arbitrary parsed-JSON data snippet."""
if isinstance(data, function.Function):

View File

@ -12,7 +12,6 @@
# under the License.
import six
import warnings
from heat.common import exception
from heat.common import template_format
@ -282,45 +281,3 @@ class ResourceDefinitionDiffTest(common.HeatTestCase):
self.assertFalse(diff.update_policy_changed())
self.assertFalse(diff.metadata_changed())
self.assertFalse(diff)
class ResourceDefinitionSnippetTest(common.HeatTestCase):
scenarios = [
('type',
dict(
defn={},
expected={})),
('metadata',
dict(
defn={'metadata': {'Foo': 'bar'}},
expected={'Metadata': {'Foo': 'bar'}})),
('properties',
dict(
defn={'properties': {'Foo': 'bar'}},
expected={'Properties': {'Foo': 'bar'}})),
('deletion_policy',
dict(
defn={'deletion_policy': rsrc_defn.ResourceDefinition.RETAIN},
expected={'DeletionPolicy': 'Retain'})),
('update_policy',
dict(
defn={'update_policy': {'Foo': 'bar'}},
expected={'UpdatePolicy': {'Foo': 'bar'}}))
]
def test_resource_snippet(self):
rd = rsrc_defn.ResourceDefinition('rsrc', 'SomeType', **self.defn)
with warnings.catch_warnings(record=True) as ws:
warnings.filterwarnings('always')
# Work around http://bugs.python.org/issue4180
getattr(rsrc_defn, '__warningregistry__', {}).clear()
exp_result = {'Type': 'SomeType'}
exp_result.update(self.expected)
self.assertEqual(exp_result, rd)
self.assertTrue(ws)
for warn in ws:
self.assertTrue(issubclass(warn.category, DeprecationWarning))