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:
parent
53482b0d37
commit
2c6fc7bcd6
|
@ -14,7 +14,6 @@ import collections
|
||||||
import copy
|
import copy
|
||||||
import itertools
|
import itertools
|
||||||
import operator
|
import operator
|
||||||
import warnings
|
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
@ -28,15 +27,15 @@ __all__ = ['ResourceDefinition']
|
||||||
|
|
||||||
|
|
||||||
@repr_wrapper
|
@repr_wrapper
|
||||||
class ResourceDefinitionCore(object):
|
class ResourceDefinition(object):
|
||||||
"""A definition of a resource, independent of any template format."""
|
"""A definition of a resource, independent of any template format."""
|
||||||
|
|
||||||
class Diff(object):
|
class Diff(object):
|
||||||
"""A diff between two versions of the same resource definition."""
|
"""A diff between two versions of the same resource definition."""
|
||||||
|
|
||||||
def __init__(self, old_defn, new_defn):
|
def __init__(self, old_defn, new_defn):
|
||||||
if not (isinstance(old_defn, ResourceDefinitionCore) and
|
if not (isinstance(old_defn, ResourceDefinition) and
|
||||||
isinstance(new_defn, ResourceDefinitionCore)):
|
isinstance(new_defn, ResourceDefinition)):
|
||||||
raise TypeError
|
raise TypeError
|
||||||
|
|
||||||
self.old_defn = old_defn
|
self.old_defn = old_defn
|
||||||
|
@ -218,8 +217,8 @@ class ResourceDefinitionCore(object):
|
||||||
function.dependencies(data, datapath))
|
function.dependencies(data, datapath))
|
||||||
|
|
||||||
explicit_depends = [] if self._depends is None else self._depends
|
explicit_depends = [] if self._depends is None else self._depends
|
||||||
prop_deps = strict_func_deps(self._properties, path(PROPERTIES))
|
prop_deps = strict_func_deps(self._properties, path('Properties'))
|
||||||
metadata_deps = strict_func_deps(self._metadata, path(METADATA))
|
metadata_deps = strict_func_deps(self._metadata, path('Metadata'))
|
||||||
|
|
||||||
# (ricolin) External resource should not depend on any other resources.
|
# (ricolin) External resource should not depend on any other resources.
|
||||||
# This operation is not allowed for now.
|
# This operation is not allowed for now.
|
||||||
|
@ -242,7 +241,7 @@ class ResourceDefinitionCore(object):
|
||||||
"""
|
"""
|
||||||
return properties.Properties(schema, self._properties or {},
|
return properties.Properties(schema, self._properties or {},
|
||||||
function.resolve, self.name, context,
|
function.resolve, self.name, context,
|
||||||
section=PROPERTIES)
|
section='Properties')
|
||||||
|
|
||||||
def deletion_policy(self):
|
def deletion_policy(self):
|
||||||
"""Return the deletion policy for the resource.
|
"""Return the deletion policy for the resource.
|
||||||
|
@ -259,7 +258,7 @@ class ResourceDefinitionCore(object):
|
||||||
"""
|
"""
|
||||||
return properties.Properties(schema, self._update_policy or {},
|
return properties.Properties(schema, self._update_policy or {},
|
||||||
function.resolve, self.name, context,
|
function.resolve, self.name, context,
|
||||||
section=UPDATE_POLICY)
|
section='UpdatePolicy')
|
||||||
|
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
"""Return the resource metadata."""
|
"""Return the resource metadata."""
|
||||||
|
@ -307,7 +306,7 @@ class ResourceDefinitionCore(object):
|
||||||
Return a Diff object that can be used to establish differences between
|
Return a Diff object that can be used to establish differences between
|
||||||
this definition and a previous definition of the same resource.
|
this definition and a previous definition of the same resource.
|
||||||
"""
|
"""
|
||||||
if not isinstance(previous, ResourceDefinitionCore):
|
if not isinstance(previous, ResourceDefinition):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
return self.Diff(previous, self)
|
return self.Diff(previous, self)
|
||||||
|
@ -320,7 +319,7 @@ class ResourceDefinitionCore(object):
|
||||||
ignored, as are the actual values that any included functions resolve
|
ignored, as are the actual values that any included functions resolve
|
||||||
to.
|
to.
|
||||||
"""
|
"""
|
||||||
if not isinstance(other, ResourceDefinitionCore):
|
if not isinstance(other, ResourceDefinition):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
return self.render_hot() == other.render_hot()
|
return self.render_hot() == other.render_hot()
|
||||||
|
@ -362,120 +361,6 @@ class ResourceDefinitionCore(object):
|
||||||
return '%(classname)s(%(name)s, %(type)s, %(args)s)' % data
|
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):
|
def _hash_data(data):
|
||||||
"""Return a stable hash value for an arbitrary parsed-JSON data snippet."""
|
"""Return a stable hash value for an arbitrary parsed-JSON data snippet."""
|
||||||
if isinstance(data, function.Function):
|
if isinstance(data, function.Function):
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import six
|
import six
|
||||||
import warnings
|
|
||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common import template_format
|
from heat.common import template_format
|
||||||
|
@ -282,45 +281,3 @@ class ResourceDefinitionDiffTest(common.HeatTestCase):
|
||||||
self.assertFalse(diff.update_policy_changed())
|
self.assertFalse(diff.update_policy_changed())
|
||||||
self.assertFalse(diff.metadata_changed())
|
self.assertFalse(diff.metadata_changed())
|
||||||
self.assertFalse(diff)
|
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))
|
|
||||||
|
|
Loading…
Reference in New Issue