components/openstack/heat/patches/04-launchpad-1496277.patch
branchs11u3-sru
changeset 6035 c9748fcc32de
equal deleted inserted replaced
6016:a477397bba8b 6035:c9748fcc32de
       
     1 This fix will be included in future 2015.1.3 (Kilo) and 5.0.1 (Liberty)
       
     2 releases.
       
     3 
       
     4 From fa19a617a79fd1cb0d892bb8ea87c4b9f6398c34 Mon Sep 17 00:00:00 2001
       
     5 From: Zane Bitter <[email protected]>
       
     6 Date: Tue, 24 Nov 2015 12:29:38 -0500
       
     7 Subject: Load template files only from their known source
       
     8 
       
     9 Modify get_class to ensure that user-defined resources cannot result in
       
    10 reads from the local filesystem. Only resources defined by the operator
       
    11 in the global environment should read local files.
       
    12 
       
    13 To make this work, this patch also adds a separate
       
    14 get_class_to_instantiate() method to the Environment.
       
    15 
       
    16 We were previously using get_class for two different purposes - to get a
       
    17 resource plugin on which we could perform introspection to obtain the
       
    18 properties and attributes schema, and to get a resource plugin we could
       
    19 instantiate to create a Resource object. These are both the same except in
       
    20 the case of a TemplateResource, where having two different use cases for
       
    21 the same piece of code was adding considerable extra complexity. Combining
       
    22 the use cases in this way also made the error handling confusing (leading
       
    23 to bug 1518458).
       
    24 
       
    25 This change separates out the two cases.
       
    26 
       
    27 Change-Id: I845e7d23c73242a4a4c9c40599690ab705c75caa
       
    28 Closes-Bug: #1496277
       
    29 Related-Bug: #1447194
       
    30 Related-Bug: #1518458
       
    31 Related-Bug: #1508115
       
    32 (cherry picked from commit 06a713c4456203cd561f16721dc8ac3bcbb37a3
       
    33                        and 26e6d5f6d776c1027c4f27058767952a58d15e25)
       
    34 ---
       
    35 
       
    36 --- heat-2015.1.2/heat/engine/environment.py.~1~	2015-10-13 09:51:53.000000000 -0700
       
    37 +++ heat-2015.1.2/heat/engine/environment.py	2016-01-25 20:50:15.096875593 -0800
       
    38 @@ -112,6 +112,12 @@ class ResourceInfo(object):
       
    39      def matches(self, resource_type):
       
    40          return False
       
    41  
       
    42 +    def get_class(self):
       
    43 +        raise NotImplemented
       
    44 +
       
    45 +    def get_class_to_instantiate(self):
       
    46 +        return self.get_class()
       
    47 +
       
    48      def __str__(self):
       
    49          return '[%s](User:%s) %s -> %s' % (self.description,
       
    50                                             self.user_resource,
       
    51 @@ -140,10 +146,20 @@ class TemplateResourceInfo(ResourceInfo)
       
    52  
       
    53      def get_class(self):
       
    54          from heat.engine.resources import template_resource
       
    55 +        if self.user_resource:
       
    56 +            allowed_schemes = template_resource.REMOTE_SCHEMES
       
    57 +        else:
       
    58 +            allowed_schemes = template_resource.LOCAL_SCHEMES
       
    59 +        data = template_resource.TemplateResource.get_template_file(
       
    60 +            self.template_name,
       
    61 +            allowed_schemes)
       
    62          env = self.registry.environment
       
    63 -        return template_resource.generate_class(str(self.name),
       
    64 -                                                self.template_name,
       
    65 -                                                env)
       
    66 +        return template_resource.generate_class_from_template(str(self.name),
       
    67 +                                                              data, env)
       
    68 +
       
    69 +    def get_class_to_instantiate(self):
       
    70 +        from heat.engine.resources import template_resource
       
    71 +        return template_resource.TemplateResource
       
    72  
       
    73  
       
    74  class MapResourceInfo(ResourceInfo):
       
    75 @@ -398,6 +414,13 @@ class ResourceRegistry(object):
       
    76                  return match
       
    77  
       
    78      def get_class(self, resource_type, resource_name=None):
       
    79 +        info = self.get_resource_info(resource_type,
       
    80 +                                      resource_name=resource_name)
       
    81 +        if info is None:
       
    82 +            raise exception.ResourceTypeNotFound(type_name=resource_type)
       
    83 +        return info.get_class()
       
    84 +
       
    85 +    def get_class_to_instantiate(self, resource_type, resource_name=None):
       
    86          if resource_type == "":
       
    87              msg = _('Resource "%s" has no type') % resource_name
       
    88              raise exception.StackValidationFailed(message=msg)
       
    89 @@ -414,7 +437,7 @@ class ResourceRegistry(object):
       
    90          if info is None:
       
    91              msg = _("Unknown resource Type : %s") % resource_type
       
    92              raise exception.StackValidationFailed(message=msg)
       
    93 -        return info.get_class()
       
    94 +        return info.get_class_to_instantiate()
       
    95  
       
    96      def as_dict(self):
       
    97          """Return user resources in a dict format."""
       
    98 @@ -521,6 +544,10 @@ class Environment(object):
       
    99      def get_class(self, resource_type, resource_name=None):
       
   100          return self.registry.get_class(resource_type, resource_name)
       
   101  
       
   102 +    def get_class_to_instantiate(self, resource_type, resource_name=None):
       
   103 +        return self.registry.get_class_to_instantiate(resource_type,
       
   104 +                                                      resource_name)
       
   105 +
       
   106      def get_types(self, support_status=None):
       
   107          return self.registry.get_types(support_status)
       
   108  
       
   109 --- heat-2015.1.2/heat/engine/resource.py.~1~	2015-10-13 09:51:53.000000000 -0700
       
   110 +++ heat-2015.1.2/heat/engine/resource.py	2016-01-25 20:50:15.097540727 -0800
       
   111 @@ -137,14 +137,11 @@ class Resource(object):
       
   112              # Call is already for a subclass, so pass it through
       
   113              ResourceClass = cls
       
   114          else:
       
   115 -            from heat.engine.resources import template_resource
       
   116 -
       
   117              registry = stack.env.registry
       
   118 -            try:
       
   119 -                ResourceClass = registry.get_class(definition.resource_type,
       
   120 -                                                   resource_name=name)
       
   121 -            except exception.NotFound:
       
   122 -                ResourceClass = template_resource.TemplateResource
       
   123 +            ResourceClass = registry.get_class_to_instantiate(
       
   124 +                definition.resource_type,
       
   125 +                resource_name=name)
       
   126 +
       
   127              assert issubclass(ResourceClass, Resource)
       
   128  
       
   129          return super(Resource, cls).__new__(ResourceClass)
       
   130 --- heat-2015.1.2/heat/engine/resources/openstack/heat/resource_group.py.~1~	2015-10-13 09:51:53.000000000 -0700
       
   131 +++ heat-2015.1.2/heat/engine/resources/openstack/heat/resource_group.py	2016-01-25 20:50:15.098020940 -0800
       
   132 @@ -193,11 +193,7 @@ class ResourceGroup(stack_resource.Stack
       
   133          val_templ = template.Template(test_tmpl)
       
   134          res_def = val_templ.resource_definitions(self.stack)["0"]
       
   135          # make sure we can resolve the nested resource type
       
   136 -        try:
       
   137 -            self.stack.env.get_class(res_def.resource_type)
       
   138 -        except exception.NotFound:
       
   139 -            # its a template resource
       
   140 -            pass
       
   141 +        self.stack.env.get_class_to_instantiate(res_def.resource_type)
       
   142  
       
   143          try:
       
   144              name = "%s-%s" % (self.stack.name, self.name)
       
   145 --- heat-2015.1.2/heat/engine/resources/template_resource.py.~1~	2015-10-13 09:51:53.000000000 -0700
       
   146 +++ heat-2015.1.2/heat/engine/resources/template_resource.py	2016-01-25 20:50:15.098440251 -0800
       
   147 @@ -26,8 +26,11 @@ from heat.engine.resources import stack_
       
   148  from heat.engine import template
       
   149  
       
   150  
       
   151 -def generate_class(name, template_name, env):
       
   152 -    data = TemplateResource.get_template_file(template_name, ('file',))
       
   153 +REMOTE_SCHEMES = ('http', 'https')
       
   154 +LOCAL_SCHEMES = ('file',)
       
   155 +
       
   156 +
       
   157 +def generate_class_from_template(name, data, env):
       
   158      tmpl = template.Template(template_format.parse(data))
       
   159      props, attrs = TemplateResource.get_schemas(tmpl, env.param_defaults)
       
   160      cls = type(name, (TemplateResource,),
       
   161 @@ -74,9 +77,9 @@ class TemplateResource(stack_resource.St
       
   162              self.template_name = tri.template_name
       
   163              self.resource_type = tri.name
       
   164              if tri.user_resource:
       
   165 -                self.allowed_schemes = ('http', 'https')
       
   166 +                self.allowed_schemes = REMOTE_SCHEMES
       
   167              else:
       
   168 -                self.allowed_schemes = ('http', 'https', 'file')
       
   169 +                self.allowed_schemes = REMOTE_SCHEMES + LOCAL_SCHEMES
       
   170  
       
   171          return tri
       
   172  
       
   173 --- heat-2015.1.2/heat/engine/service.py.~1~	2015-10-13 09:51:53.000000000 -0700
       
   174 +++ heat-2015.1.2/heat/engine/service.py	2016-01-25 20:50:15.099200696 -0800
       
   175 @@ -978,8 +978,6 @@ class EngineService(service.Service):
       
   176          """
       
   177          try:
       
   178              resource_class = resources.global_env().get_class(type_name)
       
   179 -        except exception.StackValidationFailed:
       
   180 -            raise exception.ResourceTypeNotFound(type_name=type_name)
       
   181          except exception.NotFound as ex:
       
   182              raise exception.StackValidationFailed(message=ex.message)
       
   183  
       
   184 @@ -1010,8 +1008,6 @@ class EngineService(service.Service):
       
   185          try:
       
   186              return resources.global_env().get_class(
       
   187                  type_name).resource_to_template(type_name)
       
   188 -        except exception.StackValidationFailed:
       
   189 -            raise exception.ResourceTypeNotFound(type_name=type_name)
       
   190          except exception.NotFound as ex:
       
   191              raise exception.StackValidationFailed(message=ex.message)
       
   192  
       
   193 --- heat-2015.1.2/heat/tests/test_provider_template.py.~1~	2015-10-13 09:51:54.000000000 -0700
       
   194 +++ heat-2015.1.2/heat/tests/test_provider_template.py	2016-01-25 20:50:15.099763200 -0800
       
   195 @@ -613,7 +613,11 @@ class ProviderTemplateTest(common.HeatTe
       
   196  
       
   197          env_str = {'resource_registry': {'resources': {'fred': {
       
   198              "OS::ResourceType": test_templ_name}}}}
       
   199 -        env = environment.Environment(env_str)
       
   200 +        global_env = environment.Environment({}, user_env=False)
       
   201 +        global_env.load(env_str)
       
   202 +        with mock.patch('heat.engine.resources._environment',
       
   203 +                        global_env):
       
   204 +            env = environment.Environment({})
       
   205          cls = env.get_class('OS::ResourceType', 'fred')
       
   206          self.assertNotEqual(template_resource.TemplateResource, cls)
       
   207          self.assertTrue(issubclass(cls, template_resource.TemplateResource))
       
   208 @@ -640,10 +644,6 @@ class ProviderTemplateTest(common.HeatTe
       
   209          self.assertTrue(test_templ, "Empty test template")
       
   210          self.m.StubOutWithMock(urlfetch, "get")
       
   211          urlfetch.get(test_templ_name,
       
   212 -                     allowed_schemes=('file',)
       
   213 -                     ).AndRaise(urlfetch.URLFetchError(
       
   214 -                         _('Failed to retrieve template')))
       
   215 -        urlfetch.get(test_templ_name,
       
   216                       allowed_schemes=('http', 'https')).AndReturn(test_templ)
       
   217          parsed_test_templ = template_format.parse(test_templ)
       
   218          self.m.ReplayAll()
       
   219 --- heat-2015.1.2/heat/tests/test_resource.py.~1~	2015-10-13 09:51:54.000000000 -0700
       
   220 +++ heat-2015.1.2/heat/tests/test_resource.py	2016-01-25 20:50:15.100592773 -0800
       
   221 @@ -67,12 +67,13 @@ class ResourceTest(common.HeatTestCase):
       
   222          self.patch('heat.engine.resource.warnings')
       
   223  
       
   224      def test_get_class_ok(self):
       
   225 -        cls = resources.global_env().get_class('GenericResourceType')
       
   226 +        cls = resources.global_env().get_class_to_instantiate(
       
   227 +            'GenericResourceType')
       
   228          self.assertEqual(generic_rsrc.GenericResource, cls)
       
   229  
       
   230      def test_get_class_noexist(self):
       
   231          self.assertRaises(exception.StackValidationFailed,
       
   232 -                          resources.global_env().get_class,
       
   233 +                          resources.global_env().get_class_to_instantiate,
       
   234                            'NoExistResourceType')
       
   235  
       
   236      def test_resource_new_ok(self):