docs: def _add_merged_attributes(node, all_recipes, all_roles):
      """Merges attributes from cookbooks, node and roles
  
      Chef Attribute precedence:
      http://docs.opscode.com/essentials_cookbook_attribute_files.html#attribute-precedence
      LittleChef implements, in precedence order:
          - Cookbook default
          - Environment default
          - Role default
          - Node normal
          - Role override
          - Environment override
  
      NOTE: In order for cookbook attributes to be read, they need to be
          correctly defined in its metadata.json
  
      """
      # Get cookbooks from extended recipes
      attributes = {}
      for recipe in node['recipes']:
          # Find this recipe
          found = False
          for r in all_recipes:
              if recipe == r['name']:
                  found = True
                  for attr in r['attributes']:
                      if r['attributes'][attr].get('type') == "hash":
                          value = {}
                      else:
                          value = r['attributes'][attr].get('default')
                      # Attribute dictionaries are defined as a single
                      # compound key. Split and build proper dict
                      build_dct(attributes, attr.split("/"), value)
          if not found:
              error = "Could not find recipe '{0}' while ".format(recipe)
              error += "building node data bag for '{0}'".format(node['name'])
              abort(error)
  
      # Get default role attributes
      for role in node['roles']:
          for r in all_roles:
              if role == r['name']:
                  update_dct(attributes, r.get('default_attributes', {}))
  
      # Get default environment attributes
      environment = lib.get_environment(node['chef_environment'])
      update_dct(attributes, environment.get('default_attributes', {}))
  
      # Get normal node attributes
      non_attribute_fields = [
          'id', 'name', 'role', 'roles', 'recipes', 'run_list', 'ipaddress']
      node_attributes = {}
      for key in node:
          if key in non_attribute_fields:
              continue
          node_attributes[key] = node[key]
      update_dct(attributes, node_attributes)
  
      # Get override role attributes
      for role in node['roles']:
          for r in all_roles:
              if role == r['name']:
                  update_dct(attributes, r.get('override_attributes', {}))
  
      # Get override environment attributes
      update_dct(attributes, environment.get('override_attributes', {}))
  
      # Merge back to the original node object
      node.update(attributes)