Skip to content

Commit db3eeb5

Browse files
committed
Normal, Default, and Override Attributes CHEF-838
Adds normal, default, and override attributes. Updates Chef::Node::Attribute to only set the proper attribute hash.
1 parent 75e949b commit db3eeb5

4 files changed

Lines changed: 132 additions & 71 deletions

File tree

‎chef/lib/chef/node.rb‎

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
class Chef
3434
class Node
3535

36-
attr_accessor :attribute, :recipe_list, :couchdb, :couchdb_rev, :run_state, :run_list, :override_attrs, :default_attrs, :cookbook_loader
36+
attr_accessor :recipe_list, :couchdb, :couchdb_rev, :run_state, :run_list, :override_attrs, :default_attrs, :normal_attrs, :cookbook_loader
3737
attr_reader :node
3838
attr_reader :couchdb_id
3939

@@ -131,7 +131,7 @@ def initialize(couchdb=nil)
131131
@name = nil
132132
@node = self
133133

134-
@attribute = Mash.new
134+
@normal_attrs = Mash.new
135135
@override_attrs = Mash.new
136136
@default_attrs = Mash.new
137137
@run_list = Chef::RunList.new
@@ -191,64 +191,109 @@ def name(arg=nil)
191191
@name
192192
end
193193
end
194+
195+
def attribute
196+
Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)
197+
end
194198

195199
# Return an attribute of this node. Returns nil if the attribute is not found.
196200
def [](attrib)
197-
Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs)[attrib]
201+
Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)[attrib]
198202
end
199203

200204
# Set an attribute of this node
201205
def []=(attrib, value)
202-
Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs)[attrib] = value
206+
Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)[attrib] = value
203207
end
204208

205209
def store(attrib, value)
206210
self[attrib] = value
207211
end
208212

209-
# Set an attribute of this node, but auto-vivifiy any Mashes that might
210-
# be missing
213+
# Set a normal attribute of this node, but auto-vivifiy any Mashes that
214+
# might be missing
211215
def set
212-
attrs = Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs)
216+
attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)
217+
attrs.set_type = :attribute
213218
attrs.auto_vivifiy_on_read = true
214219
attrs
215220
end
216221

217-
# Set an attribute of this node, auto-vivifiying any mashes that are
222+
alias_method :normal, :set
223+
224+
# Set a normal attribute of this node, auto-vivifiying any mashes that are
218225
# missing, but if the final value already exists, don't set it
219226
def set_unless
220-
attrs = Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs)
227+
attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)
228+
attrs.set_type = :attribute
221229
attrs.auto_vivifiy_on_read = true
222230
attrs.set_unless_value_present = true
223231
attrs
224232
end
233+
alias_method :normal_unless, :set_unless
234+
235+
# Set a default of this node, but auto-vivifiy any Mashes that might
236+
# be missing
237+
def default
238+
attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)
239+
attrs.set_type = :default
240+
attrs.auto_vivifiy_on_read = true
241+
attrs
242+
end
225243

226-
alias_method :default, :set_unless
244+
# Set a default attribute of this node, auto-vivifiying any mashes that are
245+
# missing, but if the final value already exists, don't set it
246+
def default_unless
247+
attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)
248+
attrs.set_type = :default
249+
attrs.auto_vivifiy_on_read = true
250+
attrs.set_unless_value_present = true
251+
attrs
252+
end
253+
254+
# Set an override attribute of this node, but auto-vivifiy any Mashes that
255+
# might be missing
256+
def override
257+
attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)
258+
attrs.set_type = :override
259+
attrs.auto_vivifiy_on_read = true
260+
attrs
261+
end
262+
263+
# Set an override attribute of this node, auto-vivifiying any mashes that
264+
# are missing, but if the final value already exists, don't set it
265+
def override_unless
266+
attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs)
267+
attrs.set_type = :override
268+
attrs.auto_vivifiy_on_read = true
269+
attrs.set_unless_value_present = true
270+
attrs
271+
end
227272

228273
# Return true if this Node has a given attribute, false if not. Takes either a symbol or
229274
# a string.
230275
#
231276
# Only works on the top level. Preferred way is to use the normal [] style
232277
# lookup and call attribute?()
233278
def attribute?(attrib)
234-
Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs).attribute?(attrib)
279+
Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs).attribute?(attrib)
235280
end
236281

237282
# Yield each key of the top level to the block.
238283
def each(&block)
239-
Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs).each(&block)
284+
Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs).each(&block)
240285
end
241286

242287
# Iterates over each attribute, passing the attribute and value to the block.
243288
def each_attribute(&block)
244-
Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs).each_attribute(&block)
289+
Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs).each_attribute(&block)
245290
end
246291

247292
# Set an attribute based on the missing method. If you pass an argument, we'll use that
248293
# to set the attribute values. Otherwise, we'll wind up just returning the attributes
249294
# value.
250295
def method_missing(symbol, *args)
251-
Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs).send(symbol, *args)
296+
Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs).send(symbol, *args)
252297
end
253298

254299
# Returns true if this Node expects a given recipe, false if not.
@@ -295,11 +340,11 @@ def consume_attributes(attrs)
295340
# Transform the node to a Hash
296341
def to_hash
297342
index_hash = Hash.new
298-
self.each do |k, v|
299-
index_hash[k] = v
300-
end
301343
index_hash["chef_type"] = "node"
302344
index_hash["name"] = @name
345+
index_hash["attributes"] = @normal_attrs
346+
index_hash["defaults"] = @default_attrs
347+
index_hash["overrides"] = @override_attrs
303348
index_hash["recipe"] = @run_list.recipes if @run_list.recipes.length > 0
304349
index_hash["role"] = @run_list.roles if @run_list.roles.length > 0
305350
index_hash["run_list"] = @run_list.run_list if @run_list.run_list.length > 0
@@ -311,7 +356,7 @@ def to_json(*a)
311356
result = {
312357
"name" => @name,
313358
'json_class' => self.class.name,
314-
"attributes" => @attribute,
359+
"attributes" => @normal_attrs,
315360
"chef_type" => "node",
316361
"defaults" => @default_attrs,
317362
"overrides" => @override_attrs,
@@ -325,8 +370,7 @@ def to_json(*a)
325370
def self.json_create(o)
326371
node = new
327372
node.name(o["name"])
328-
o["attributes"].each { |k,v| node[k] = v }
329-
373+
node.normal_attrs = Mash.new(o["attributes"]) if o.has_key?("attributes")
330374
node.default_attrs = Mash.new(o["defaults"]) if o.has_key?("defaults")
331375
node.override_attrs = Mash.new(o["overrides"]) if o.has_key?("overrides")
332376

‎chef/lib/chef/node/attribute.rb‎

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class Attribute
3434
:set_unless_value_present,
3535
:has_been_read
3636

37+
attr_writer :set_type
38+
3739
include Enumerable
3840

3941
def initialize(attribute, default, override, state=[])
@@ -46,9 +48,21 @@ def initialize(attribute, default, override, state=[])
4648
@state = state
4749
@auto_vivifiy_on_read = false
4850
@set_unless_value_present = false
51+
@set_type = :attribute
4952
@has_been_read = false
5053
end
5154

55+
def set_type
56+
case @set_type
57+
when :attribute
58+
@attribute
59+
when :override
60+
@override
61+
when :default
62+
@default
63+
end
64+
end
65+
5266
# Reset our internal state to the top of every tree
5367
def reset
5468
@current_attribute = @attribute
@@ -280,16 +294,8 @@ def determine_value(o_value, a_value, d_value)
280294

281295
def []=(key, value)
282296
if set_unless_value_present
283-
if get_value(@default, key) != nil
284-
Chef::Log.debug("Not setting #{state.join("/")}/#{key} to #{value.inspect} because it has a default value already")
285-
return false
286-
end
287-
if get_value(@attribute, key) != nil
288-
Chef::Log.debug("Not setting #{state.join("/")}/#{key} to #{value.inspect} because it has a node attribute value already")
289-
return false
290-
end
291-
if get_value(@override, key) != nil
292-
Chef::Log.debug("Not setting #{state.join("/")}/#{key} to #{value.inspect} because it has an override value already")
297+
if get_value(set_type, key) != nil
298+
Chef::Log.debug("Not setting #{state.join("/")}/#{key} to #{value.inspect} because it has a #{@set_type} value already")
293299
return false
294300
end
295301
end
@@ -302,8 +308,7 @@ def []=(key, value)
302308
# supporting one more single-use style.
303309
@state.pop if @has_been_read && @state.last == key
304310

305-
set_value(@attribute, key, value)
306-
set_value(@override, key, value)
311+
set_value(set_type, key, value)
307312
value
308313
end
309314

‎chef/spec/unit/node/attribute_spec.rb‎

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,6 @@
330330
@attributes["longboard"] = "surfing"
331331
@attributes["longboard"].should == "surfing"
332332
@attributes.attribute["longboard"].should == "surfing"
333-
@attributes.override["longboard"].should == "surfing"
334333
end
335334

336335
it "should set deeply nested attribute value when auto_vivifiy_on_read is true" do
@@ -357,24 +356,6 @@
357356
@attributes["hostname"].should == "latte"
358357
end
359358

360-
it "should optionally skip setting the value if a default already exists" do
361-
@attributes.set_unless_value_present = true
362-
@attributes["music"]["mastodon"] = "slays it"
363-
@attributes["music"]["mastodon"].should == "rocks"
364-
end
365-
366-
it "should optionally skip setting the value if an attibute already exists" do
367-
@attributes.set_unless_value_present = true
368-
@attributes["network"]["default_interface"] = "wiz1"
369-
@attributes["network"]["default_interface"].should == "en1"
370-
end
371-
372-
it "should optionally skip setting the value if an override already exists" do
373-
@attributes.set_unless_value_present = true
374-
@attributes["fire"] = "secret life"
375-
@attributes["fire"].should == "still burn"
376-
end
377-
378359
it "should write to an attribute that has been read before properly" do
379360
@attributes["foo"] = Mash.new
380361
@attributes["foo"]["bar"] ||= "stop the world"

‎chef/spec/unit/node_spec.rb‎

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -101,26 +101,58 @@
101101
@node.sunshine.should eql("is bright")
102102
end
103103

104-
it "should allow you to set an attribute with set, without pre-declaring a hash" do
105-
@node.set[:snoopy][:is_a_puppy] = true
106-
@node[:snoopy][:is_a_puppy].should == true
107-
end
104+
describe "normal attributes" do
105+
it "should allow you to set an attribute with set, without pre-declaring a hash" do
106+
@node.set[:snoopy][:is_a_puppy] = true
107+
@node[:snoopy][:is_a_puppy].should == true
108+
end
109+
110+
it "should allow you to set an attribute with set_unless" do
111+
@node.set_unless[:snoopy][:is_a_puppy] = false
112+
@node[:snoopy][:is_a_puppy].should == false
113+
end
108114

109-
it "should allow you to set an attribute with set_unless, without pre-declaring a hash, but only if the value is not already set" do
110-
@node.set[:snoopy][:is_a_puppy] = true
111-
@node.set_unless[:snoopy][:is_a_puppy] = false
112-
@node[:snoopy][:is_a_puppy].should == true
115+
it "should not allow you to set an attribute with set_unless if it already exists" do
116+
@node.set[:snoopy][:is_a_puppy] = true
117+
@node.set_unless[:snoopy][:is_a_puppy] = false
118+
@node[:snoopy][:is_a_puppy].should == true
119+
end
113120
end
114121

115-
it "should allow you to set an attribute with default, without pre-declaring a hash, but only if the value is not already set" do
116-
@node.set[:snoopy][:is_a_puppy] = true
117-
@node.default[:snoopy][:is_a_puppy] = false
118-
@node[:snoopy][:is_a_puppy].should == true
122+
describe "default attributes" do
123+
it "should be set with default, without pre-declaring a hash" do
124+
@node.default[:snoopy][:is_a_puppy] = true
125+
@node[:snoopy][:is_a_puppy].should == true
126+
end
127+
128+
it "should allow you to set with default_unless without pre-declaring a hash" do
129+
@node.default_unless[:snoopy][:is_a_puppy] = false
130+
@node[:snoopy][:is_a_puppy].should == false
131+
end
132+
133+
it "should not allow you to set an attribute with default_unless if it already exists" do
134+
@node.default[:snoopy][:is_a_puppy] = true
135+
@node.default_unless[:snoopy][:is_a_puppy] = false
136+
@node[:snoopy][:is_a_puppy].should == true
137+
end
119138
end
120139

121-
it "should allow you to set an attribute with default, without pre-declaring a hash" do
122-
@node.default[:snoopy][:is_a_puppy] = false
123-
@node[:snoopy][:is_a_puppy].should == false
140+
describe "override attributes" do
141+
it "should be set with override, without pre-declaring a hash" do
142+
@node.override[:snoopy][:is_a_puppy] = true
143+
@node[:snoopy][:is_a_puppy].should == true
144+
end
145+
146+
it "should allow you to set with override_unless without pre-declaring a hash" do
147+
@node.override_unless[:snoopy][:is_a_puppy] = false
148+
@node[:snoopy][:is_a_puppy].should == false
149+
end
150+
151+
it "should not allow you to set an attribute with override_unless if it already exists" do
152+
@node.override[:snoopy][:is_a_puppy] = true
153+
@node.override_unless[:snoopy][:is_a_puppy] = false
154+
@node[:snoopy][:is_a_puppy].should == true
155+
end
124156
end
125157

126158
it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do
@@ -290,15 +322,14 @@
290322
it "should serialize itself as a hash" do
291323
@node.default_attrs = { "one" => { "two" => "three", "four" => "five", "eight" => "nine" } }
292324
@node.override_attrs = { "one" => { "two" => "three", "four" => "six" } }
293-
@node.set["one"]["two"] = "seven"
325+
@node.normal_attrs = { "one" => { "two" => "seven" } }
294326
@node.run_list << "role[marxist]"
295327
@node.run_list << "role[leninist]"
296328
@node.run_list << "recipe[stalinist]"
297329
h = @node.to_hash
298-
h["one"]["two"].should == "seven"
299-
h["one"]["four"].should == "six"
300-
h["one"]["eight"].should == "nine"
301-
h["recipe"].should be_include("stalinist")
330+
h["defaults"].should == @node.default_attrs
331+
h["overrides"].should == @node.override_attrs
332+
h["attributes"].should == @node.normal_attrs
302333
h["role"].should be_include("marxist")
303334
h["role"].should be_include("leninist")
304335
h["run_list"].should be_include("role[marxist]")

0 commit comments

Comments
 (0)