diff options
15 files changed, 151 insertions, 111 deletions
diff --git a/chef-server-api/app/controllers/environments.rb b/chef-server-api/app/controllers/environments.rb index f3b207db90..d5e20171fe 100644 --- a/chef-server-api/app/controllers/environments.rb +++ b/chef-server-api/app/controllers/environments.rb @@ -111,7 +111,48 @@ class Environments < Application }) end - # POST /environment/:environment_id/cookbook_versions + # GET /environments/:environment_id/cookbooks/:cookbook_id + # returns data in the format of: + # {"apache2" => { + # :url => "http://url", + # :versions => [{:url => "http://url/1.0.0", :version => "1.0.0"}, {:url => "http://url/0.0.1", :version=>"0.0.1"}] + # } + # } + def cookbook + cookbook_name = params[:cookbook_id] + begin + filtered_cookbooks = Chef::Environment.cdb_load_filtered_cookbook_versions(params[:environment_id]) + rescue Chef::Exceptions::CouchDBNotFound + raise NotFound, "Cannot load environment #{params[:environment_id]}" + end + raise NotFound, "Cannot load cookbook #{cookbook_name}" unless filtered_cookbooks.has_key?(cookbook_name) + versions = filtered_cookbooks[cookbook_name].map{|v| v.version.to_s} + num_versions = num_versions!("all") + display({ cookbook_name => expand_cookbook_urls(cookbook_name, versions, num_versions) }) + end + + # GET /environments/:environment/recipes + def list_recipes + display(Chef::Environment.cdb_load_filtered_recipe_list(params[:environment_id])) + end + + # GET /environments/:environment_id/nodes + def list_nodes + node_list = Chef::Node.cdb_list_by_environment(params[:environment_id]) + display(node_list.inject({}) {|r,n| r[n] = absolute_url(:node, n); r}) + end + + # GET /environments/:environment_id/roles/:role_id + def role + begin + role = Chef::Role.cdb_load(params[:role_id]) + rescue Chef::Exceptions::CouchDBNotFound + raise NotFound, "Cannot load role #{params[:role_id]}" + end + display("run_list" => role.env_run_lists[params[:environment_id]]) + end + + # POST /environments/:environment_id/cookbook_versions # # Take the given run_list and return the versions of cookbooks that would # be used after applying the constraints of the given environment. @@ -158,54 +199,9 @@ class Environments < Application # to # name => cookbook manifest # and display. - display(names_to_cookbook_version.inject({}) do |res, (cb_name, cb_version)| - res[cb_name] = cb_version.generate_manifest_with_urls {|opts| absolute_url(:cookbook_file, opts) } + display(names_to_cookbook_version.inject({}) do |res, (cookbook_name, cookbook_version)| + res[cookbook_name] = cookbook_version.generate_manifest_with_urls {|opts| absolute_url(:cookbook_file, opts) } res end) end - - - # GET /environments/:environment_id/cookbooks/:cookbook_id - # returns data in the format of: - # {"apache2" => { - # :url => "http://url", - # :versions => [{:url => "http://url/1.0.0", :version => "1.0.0"}, {:url => "http://url/0.0.1", :version=>"0.0.1"}] - # } - # } - def cookbook - cookbook_name = params[:cookbook_id] - begin - filtered_cookbooks = Chef::Environment.cdb_load_filtered_cookbook_versions(params[:environment_id]) - rescue Chef::Exceptions::CouchDBNotFound - raise NotFound, "Cannot load environment #{params[:environment_id]}" - end - raise NotFound, "Cannot load cookbook #{cookbook_name}" unless filtered_cookbooks.has_key?(cookbook_name) - versions = filtered_cookbooks[cookbook_name].map{|v| v.version.to_s} - num_versions = num_versions!("all") - display({ cookbook_name => expand_cookbook_urls(cookbook_name, versions, num_versions) }) - end - - # GET /environments/:environment/recipes - def list_recipes - display(Chef::Environment.cdb_load_filtered_recipe_list(params[:environment_id])) - end - - # GET /environments/:environment_id/nodes - def list_nodes - node_list = Chef::Node.cdb_list_by_environment(params[:environment_id]) - display(node_list.inject({}) {|r,n| r[n] = absolute_url(:node, n); r}) - end - - # GET /environments/:environment_id/roles/:role_id - def role - begin - role = Chef::Role.cdb_load(params[:role_id]) - rescue Chef::Exceptions::CouchDBNotFound - raise NotFound, "Cannot load role #{params[:role_id]}" - end - display("run_list" => role.env_run_lists[params[:environment_id]]) - end - - private - end diff --git a/chef-server-api/app/controllers/nodes.rb b/chef-server-api/app/controllers/nodes.rb index e71b8acea1..72564f02fe 100644 --- a/chef-server-api/app/controllers/nodes.rb +++ b/chef-server-api/app/controllers/nodes.rb @@ -106,8 +106,8 @@ class Nodes < Application # to # name => cookbook manifest # and display. - display(included_cookbooks.inject({}) do |acc, (cookbook_name, cookbook)| - acc[cookbook_name.to_s] = cookbook.generate_manifest_with_urls{|opts| absolute_url(:cookbook_file, opts) } + display(included_cookbooks.inject({}) do |acc, (cookbook_name, cookbook_version)| + acc[cookbook_name.to_s] = cookbook_version.generate_manifest_with_urls{|opts| absolute_url(:cookbook_file, opts) } acc end) end diff --git a/chef/lib/chef/client.rb b/chef/lib/chef/client.rb index 811fd125af..fd673545f3 100644 --- a/chef/lib/chef/client.rb +++ b/chef/lib/chef/client.rb @@ -186,7 +186,7 @@ class Chef run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new(cookbook_hash)) end run_status.run_context = run_context - run_context.load(@expanded_run_list) + run_context.load(@run_list_expansion) assert_cookbook_path_not_empty(run_context) run_context end @@ -228,18 +228,35 @@ class Chef @node = Chef::Node.find_or_create(node_name) end + # Allow user to override the environment of a node by specifying + # a config parameter. + if Chef::Config[:environment] && !Chef::Config[:environment].chop.empty? + @node.chef_environment(Chef::Config[:environment]) + end + # consume_external_attrs may add items to the run_list. Save the # expanded run_list, which we will pass to the server later to # determine which versions of cookbooks to use. + @node.reset_defaults_and_overrides @node.consume_external_attrs(ohai.data, @json_attribs) if Chef::Config[:solo] - @expanded_run_list = @node.expand!('disk') + @run_list_expansion = @node.expand!('disk') else - @expanded_run_list = @node.expand!('server') + @run_list_expansion = @node.expand!('server') end + # @run_list_expansion is a RunListExpansion. + # + # Convert @expanded_run_list, which is an + # Array of Hashes of the form + # {:name => NAME, :version_constraint => Chef::VersionConstraint }, + # into @expanded_run_list_with_versions, an + # Array of Strings of the form + # "#{NAME}@#{VERSION}" + @expanded_run_list_with_versions = @run_list_expansion.recipes.with_version_constraints_strings + Chef::Log.info("Run List is [#{@node.run_list}]") - Chef::Log.info("Run List expands to [#{@expanded_run_list.join(', ')}]") + Chef::Log.info("Run List expands (with versions) to [#{@expanded_run_list_with_versions.join(', ')}]") @run_status = Chef::RunStatus.new(@node) @@ -270,7 +287,7 @@ class Chef def sync_cookbooks Chef::Log.debug("Synchronizing cookbooks") cookbook_hash = rest.post_rest("environments/#{@node.chef_environment}/cookbook_versions", - {:run_list => @expanded_run_list}) + {:run_list => @expanded_run_list_with_versions}) Chef::CookbookVersion.sync_cookbooks(cookbook_hash) # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks diff --git a/chef/lib/chef/exceptions.rb b/chef/lib/chef/exceptions.rb index 82b1ed10f3..2cdccbe0b1 100644 --- a/chef/lib/chef/exceptions.rb +++ b/chef/lib/chef/exceptions.rb @@ -135,7 +135,7 @@ class Chef def to_json(*a) result = { - "message" => e.message, + "message" => message, "unsatisfiable_run_list_item" => run_list_item, "non_existent_cookbooks" => non_existent_cookbooks, "most_constrained_cookbooks" => most_constrained_cookbooks diff --git a/chef/lib/chef/node.rb b/chef/lib/chef/node.rb index befc1c5075..c15d660101 100644 --- a/chef/lib/chef/node.rb +++ b/chef/lib/chef/node.rb @@ -406,11 +406,19 @@ class Chef attrs end + # Clear defaults and overrides, so that any deleted attributes + # between runs are still gone. + def reset_defaults_and_overrides + @default_attrs = Mash.new + @override_attrs = Mash.new + end + # Expands the node's run list and sets the default and override # attributes. Also applies stored attributes (from json provided # on the command line) # - # Returns the fully-expanded list of recipes. + # Returns the fully-expanded list of recipes, a RunListExpansion. + # #-- # TODO: timh/cw, 5-14-2010: Should this method exist? Should we # instead modify default_attrs and override_attrs whenever our @@ -418,21 +426,23 @@ class Chef # on-demand generation of default_attrs and override_attrs, # invalidated only when run_list is mutated? def expand!(data_source = 'server') - # This call should only be called on a chef-client run expansion = run_list.expand(chef_environment, data_source) raise Chef::Exceptions::MissingRole if expansion.errors? - @default_attrs = Mash.new - @override_attrs = Mash.new - self[:tags] = Array.new unless attribute?(:tags) - @default_attrs = expansion.default_attrs - environment_attrs = chef_environment == "_default" ? {} : Chef::Environment.load(chef_environment).attributes - @override_attrs = Chef::Mixin::DeepMerge.merge(expansion.override_attrs, environment_attrs) @automatic_attrs[:recipes] = expansion.recipes @automatic_attrs[:roles] = expansion.roles - expansion.recipes + expansion + end + + # Apply the default and overrides attributes from the expansion + # passed in, which came from roles. + def apply_expansion_attributes(expansion) + @default_attrs = Chef::Mixin::DeepMerge.merge(default_attrs, expansion.default_attrs) + environment_attrs = chef_environment == "_default" ? {} : Chef::Environment.load(chef_environment).attributes + overrides_before_environments = Chef::Mixin::DeepMerge.merge(override_attrs, expansion.override_attrs) + @override_attrs = Chef::Mixin::DeepMerge.merge(overrides_before_environments, environment_attrs) end # Transform the node to a Hash @@ -550,9 +560,7 @@ class Chef end def self.find_or_create(node_name) - node = load(node_name) - node.chef_environment(Chef::Config[:environment]) unless Chef::Config[:environment].nil? || Chef::Config[:environment].chop.empty? - node + load(node_name) rescue Net::HTTPServerException => e raise unless e.response.code == '404' node = build(node_name) diff --git a/chef/lib/chef/run_context.rb b/chef/lib/chef/run_context.rb index 961022408c..0f96c6e487 100644 --- a/chef/lib/chef/run_context.rb +++ b/chef/lib/chef/run_context.rb @@ -53,14 +53,20 @@ class Chef node.cookbook_collection = cookbook_collection end - def load(expanded_recipe_names) + def load(run_list_expansion) load_libraries load_lwrp_providers load_lwrp_resources load_attributes load_resource_definitions - expanded_recipe_names.each do |recipe| + # Precendence rules state that roles' attributes come after + # cookbooks. Now we've loaded attributes from cookbooks with + # load_attributes, apply the expansion attributes (loaded from + # roles) to the node. + @node.apply_expansion_attributes(run_list_expansion) + + run_list_expansion.recipes.each do |recipe| # TODO: timh/cw, 5-14-2010: It's distasteful to be including # the DSL in a class outside the context of the DSL include_recipe(recipe) diff --git a/chef/lib/chef/run_list/versioned_recipe_list.rb b/chef/lib/chef/run_list/versioned_recipe_list.rb index c32afa0438..0eefded964 100644 --- a/chef/lib/chef/run_list/versioned_recipe_list.rb +++ b/chef/lib/chef/run_list/versioned_recipe_list.rb @@ -18,6 +18,8 @@ require 'chef/version_class' require 'chef/version_constraint' +# Why does this class exist? +# Why did we not just modify RunList/RunListItem? class Chef class RunList class VersionedRecipeList < Array @@ -38,13 +40,27 @@ class Chef end def with_versions - self.map {|i| {:name => i, :version => @versions[i]}} + self.map {|recipe_name| {:name => recipe_name, :version => @versions[recipe_name]}} end + # Return an Array of Hashes, each of the form: + # {:name => RECIPE_NAME, :version_constraint => Chef::VersionConstraint } def with_version_constraints - self.map do |i| - constraint = Chef::VersionConstraint.new(@versions[i]) - { :name => i, :version_constraint => constraint } + self.map do |recipe_name| + constraint = Chef::VersionConstraint.new(@versions[recipe_name]) + { :name => recipe_name, :version_constraint => constraint } + end + end + + # Return an Array of Strings, each of the form: + # "NAME@VERSION" + def with_version_constraints_strings + self.map do |recipe_name| + if @versions[recipe_name] + "#{recipe_name}@#{@versions[recipe_name]}" + else + recipe_name + end end end end diff --git a/features/chef-client/attribute_settings.feature b/features/chef-client/attribute_settings.feature index 728f19c7bc..e2c9a6d1c4 100644 --- a/features/chef-client/attribute_settings.feature +++ b/features/chef-client/attribute_settings.feature @@ -10,9 +10,9 @@ Feature: Set default, normal, and override attributes And it includes the recipe 'attribute_settings' When I run the chef-client Then the run should exit '0' - Then a file named 'attribute_setting.txt' should contain '1' + Then a file named 'attribute_setting.txt' should contain 'came from recipe\[attribute_settings\] attributes' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '1' + Then the inflated responses key 'attribute_priority_was' should match 'came from recipe\[attribute_settings\] attributes' Scenario: Set the default attribute in a role Given a 'role' named 'attribute_settings_default' exists @@ -20,9 +20,9 @@ Feature: Set default, normal, and override attributes And it includes the role 'attribute_settings_default' When I run the chef-client Then the run should exit '0' - And a file named 'attribute_setting.txt' should contain '2' + And a file named 'attribute_setting.txt' should contain 'came from role\[attribute_settings_default\] default attributes' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '2' + Then the inflated responses key 'attribute_priority_was' should match 'came from role\[attribute_settings_default\] default attributes' Scenario: Set the default attribute in a recipe Given a 'role' named 'attribute_settings_default' exists @@ -31,9 +31,9 @@ Feature: Set default, normal, and override attributes And it includes the recipe 'attribute_settings::default_in_recipe' When I run the chef-client Then the run should exit '0' - And a file named 'attribute_setting.txt' should contain '3' + And a file named 'attribute_setting.txt' should contain 'came from recipe\[attribute_settings::default_in_recipe\]' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '3' + Then the inflated responses key 'attribute_priority_was' should match 'came from recipe\[attribute_settings::default_in_recipe\]' Scenario: Set a normal attribute in a cookbook attribute file Given a validated node @@ -43,9 +43,9 @@ Feature: Set default, normal, and override attributes And it includes the recipe 'attribute_settings_normal' When I run the chef-client Then the run should exit '0' - Then a file named 'attribute_setting.txt' should contain '4' + Then a file named 'attribute_setting.txt' should contain 'came from recipe\[attribute_settings_normal\] attributes' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '4' + Then the inflated responses key 'attribute_priority_was' should match 'came from recipe\[attribute_settings_normal\] attributes' Scenario: Set a normal attribute in a cookbook recipe Given a validated node @@ -55,11 +55,11 @@ Feature: Set default, normal, and override attributes And it includes the recipe 'attribute_settings_normal::normal_in_recipe' When I run the chef-client Then the run should exit '0' - Then a file named 'attribute_setting.txt' should contain '5' + Then a file named 'attribute_setting.txt' should contain 'came from recipe\[attribute_settings_normal::normal_in_recipe\]' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '5' + Then the inflated responses key 'attribute_priority_was' should match 'came from recipe\[attribute_settings_normal::normal_in_recipe\]' - Scenario: Set a override attribute in a cookbook attribute file + Scenario: Set an override attribute in a cookbook attribute file Given a validated node And a 'role' named 'attribute_settings_default' exists And it includes the role 'attribute_settings_default' @@ -68,9 +68,9 @@ Feature: Set default, normal, and override attributes And it includes the recipe 'attribute_settings_override' When I run the chef-client Then the run should exit '0' - Then a file named 'attribute_setting.txt' should contain '6' + Then a file named 'attribute_setting.txt' should contain 'came from recipe\[attribute_settings_override\] override attributes' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '6' + Then the inflated responses key 'attribute_priority_was' should match 'came from recipe\[attribute_settings_override\] override attributes' Scenario: Set the override attribute in a role Given a 'role' named 'attribute_settings_default' exists @@ -83,9 +83,9 @@ Feature: Set default, normal, and override attributes And it includes the role 'attribute_settings_override' When I run the chef-client Then the run should exit '0' - And a file named 'attribute_setting.txt' should contain '7' + And a file named 'attribute_setting.txt' should contain 'came from role\[attribute_settings_override\] override attributes' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '7' + Then the inflated responses key 'attribute_priority_was' should match 'came from role\[attribute_settings_override\] override attributes' Scenario: Set the attribute in a environment Given an 'environment' named 'cucumber' exists @@ -99,9 +99,9 @@ Feature: Set default, normal, and override attributes And it includes the role 'attribute_settings_override' When I run the chef-client with '-l debug' Then the run should exit '0' - And a file named 'attribute_setting.txt' should contain '8' + And a file named 'attribute_setting.txt' should contain 'came from environment cucumber' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '8' + Then the inflated responses key 'attribute_priority_was' should match 'came from environment cucumber' Scenario: Set the override attribute in a recipe Given a 'role' named 'attribute_settings_default' exists @@ -115,9 +115,9 @@ Feature: Set default, normal, and override attributes And it includes the recipe 'attribute_settings_override::override_in_recipe' When I run the chef-client Then the run should exit '0' - And a file named 'attribute_setting.txt' should contain '9' + And a file named 'attribute_setting.txt' should contain 'came from recipe\[attribute_settings_override::override_in_recipe\]' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '9' + Then the inflated responses key 'attribute_priority_was' should match 'came from recipe\[attribute_settings_override::override_in_recipe\]' Scenario: Data is removed from override attribute in a recipe Given a 'role' named 'attribute_settings_override' exists @@ -125,9 +125,9 @@ Feature: Set default, normal, and override attributes And it includes the role 'attribute_settings_override' When I run the chef-client Then the run should exit '0' - And a file named 'attribute_setting.txt' should contain '7' + And a file named 'attribute_setting.txt' should contain 'came from role\[attribute_settings_override\] override attributes' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '7' + Then the inflated responses key 'attribute_priority_was' should match 'came from role\[attribute_settings_override\] override attributes' Given it includes no recipes And it includes the recipe 'integration_setup' And it includes the recipe 'no_attributes' @@ -143,9 +143,9 @@ Feature: Set default, normal, and override attributes And it includes the recipe 'attribute_settings_normal' When I run the chef-client with json attributes Then the run should exit '0' - Then a file named 'attribute_setting.txt' should contain '4' + Then a file named 'attribute_setting.txt' should contain 'came from recipe\[attribute_settings_normal\] attributes' When the node is retrieved from the API - Then the inflated responses key 'attribute_priority_was' should be the integer '4' + Then the inflated responses key 'attribute_priority_was' should match 'came from recipe\[attribute_settings_normal\] attributes' @chef1286 Scenario: Attributes from JSON files have higher precedence than defaults diff --git a/features/data/cookbooks/attribute_settings/attributes/default.rb b/features/data/cookbooks/attribute_settings/attributes/default.rb index 22b40329cf..735f7c82bf 100644 --- a/features/data/cookbooks/attribute_settings/attributes/default.rb +++ b/features/data/cookbooks/attribute_settings/attributes/default.rb @@ -1,2 +1,3 @@ -default[:attribute_priority_was] = 1 +default[:attribute_priority_was] = "came from recipe[attribute_settings] attributes" + diff --git a/features/data/cookbooks/attribute_settings/recipes/default_in_recipe.rb b/features/data/cookbooks/attribute_settings/recipes/default_in_recipe.rb index df4fe70f57..611f00fab9 100644 --- a/features/data/cookbooks/attribute_settings/recipes/default_in_recipe.rb +++ b/features/data/cookbooks/attribute_settings/recipes/default_in_recipe.rb @@ -17,7 +17,7 @@ # limitations under the License. # -node.default[:attribute_priority_was] = 3 +node.default[:attribute_priority_was] = "came from recipe[attribute_settings::default_in_recipe]" execute "echo #{node[:attribute_priority_was]} > #{node[:tmpdir]}/attribute_setting.txt" diff --git a/features/data/cookbooks/attribute_settings_normal/attributes/default.rb b/features/data/cookbooks/attribute_settings_normal/attributes/default.rb index 88d1e0f9e5..9a9fabd4ff 100644 --- a/features/data/cookbooks/attribute_settings_normal/attributes/default.rb +++ b/features/data/cookbooks/attribute_settings_normal/attributes/default.rb @@ -1,2 +1,2 @@ -normal[:attribute_priority_was] = 4 +normal[:attribute_priority_was] = "came from recipe[attribute_settings_normal] attributes" diff --git a/features/data/cookbooks/attribute_settings_normal/recipes/normal_in_recipe.rb b/features/data/cookbooks/attribute_settings_normal/recipes/normal_in_recipe.rb index a30df1f8c0..5badb699dc 100644 --- a/features/data/cookbooks/attribute_settings_normal/recipes/normal_in_recipe.rb +++ b/features/data/cookbooks/attribute_settings_normal/recipes/normal_in_recipe.rb @@ -17,7 +17,7 @@ # limitations under the License. # -node.normal[:attribute_priority_was] = 5 +node.normal[:attribute_priority_was] = "came from recipe[attribute_settings_normal::normal_in_recipe]" execute "echo #{node[:attribute_priority_was]} > #{node[:tmpdir]}/attribute_setting.txt" diff --git a/features/data/cookbooks/attribute_settings_override/attributes/default.rb b/features/data/cookbooks/attribute_settings_override/attributes/default.rb index fc7f0c4842..a0caa1b4b8 100644 --- a/features/data/cookbooks/attribute_settings_override/attributes/default.rb +++ b/features/data/cookbooks/attribute_settings_override/attributes/default.rb @@ -1,2 +1,2 @@ -override[:attribute_priority_was] = 6 +override[:attribute_priority_was] = "came from recipe[attribute_settings_override] override attributes" diff --git a/features/data/cookbooks/attribute_settings_override/recipes/override_in_recipe.rb b/features/data/cookbooks/attribute_settings_override/recipes/override_in_recipe.rb index cc944749f2..88a28dce0e 100644 --- a/features/data/cookbooks/attribute_settings_override/recipes/override_in_recipe.rb +++ b/features/data/cookbooks/attribute_settings_override/recipes/override_in_recipe.rb @@ -1,8 +1,4 @@ -# -# Cookbook Name:: attribute_settings -# Recipe:: default -# -# Copyright 2009, Opscode +# Copyright (c) 2009, 2011 Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,7 +13,7 @@ # limitations under the License. # -node.override[:attribute_priority_was] = 9 +node.override[:attribute_priority_was] = "came from recipe[attribute_settings_override::override_in_recipe]" execute "echo #{node[:attribute_priority_was]} > #{node[:tmpdir]}/attribute_setting.txt" diff --git a/features/steps/fixture_steps.rb b/features/steps/fixture_steps.rb index 4e339948c7..353cff2f5a 100644 --- a/features/steps/fixture_steps.rb +++ b/features/steps/fixture_steps.rb @@ -124,7 +124,7 @@ Before do r.name "attribute_settings_default" r.description "sets a default value" r.run_list("recipe[attribute_settings]") - r.default_attributes({ 'attribute_priority_was' => 2 }) + r.default_attributes({ 'attribute_priority_was' => "came from role[attribute_settings_default] default attributes" }) r end, 'attribute_settings_override' => Proc.new do @@ -132,7 +132,7 @@ Before do r.name "attribute_settings_override" r.description "sets a default value" r.run_list("recipe[attribute_settings_override]") - r.override_attributes({ 'attribute_priority_was' => 7 }) + r.override_attributes({ 'attribute_priority_was' => "came from role[attribute_settings_override] override attributes" }) r end, 'role1_includes_role2' => Proc.new do @@ -274,7 +274,7 @@ Before do e = Chef::Environment.new e.name 'cucumber' e.description 'I like to run tests' - e.attributes({"attribute_priority_was" => 8}) + e.attributes({"attribute_priority_was" => "came from environment cucumber"}) e end, 'production' => Proc.new do |