diff options
author | Robert Mullins <rmullins@secureworks.com> | 2017-03-03 14:39:07 -0500 |
---|---|---|
committer | Robert Mullins <rmullins@secureworks.com> | 2017-03-08 11:41:56 -0500 |
commit | 848fbf3c081dabe4a7f18a42597b1a68f68b6009 (patch) | |
tree | c7d49291f920dbde14b43c70953b3c1f9bc800b1 | |
parent | 5dc375d6dd7acb1d32f06b3ee663201d0a119ccf (diff) | |
download | chef-848fbf3c081dabe4a7f18a42597b1a68f68b6009.tar.gz |
Add the ability to blacklist attributes
Signed-off-by: Robert Mullins <rmullins@secureworks.com>
-rw-r--r-- | chef-config/lib/chef-config/config.rb | 9 | ||||
-rw-r--r-- | lib/chef/blacklist.rb | 81 | ||||
-rw-r--r-- | lib/chef/node.rb | 8 | ||||
-rw-r--r-- | spec/unit/node_spec.rb | 75 |
4 files changed, 173 insertions, 0 deletions
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index 3a55c8233d..51d019c432 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -835,6 +835,15 @@ module ChefConfig default :normal_attribute_whitelist, nil default :override_attribute_whitelist, nil + # A blacklisted array of attributes you do not want to send over the + # wire when node data is saved + # The default setting is nil, which collects all data. Setting to [] will + # still collect all data for save + default :automatic_attribute_blacklist, nil + default :default_attribute_blacklist, nil + default :normal_attribute_blacklist, nil + default :override_attribute_blacklist, nil + # Pull down all the rubygems versions from rubygems and cache them the first time we do a gem_package or # chef_gem install. This is memory-expensive and will grow without bounds, but will reduce network # round trips. diff --git a/lib/chef/blacklist.rb b/lib/chef/blacklist.rb new file mode 100644 index 0000000000..a60e0318cd --- /dev/null +++ b/lib/chef/blacklist.rb @@ -0,0 +1,81 @@ + +require "chef/exceptions" + +class Chef + class Blacklist + + # filter takes two arguments - the data you want to filter, and a blacklisted array + # of keys you want discluded. You can capture a subtree of the data to filter by + # providing a "/"-delimited string of keys. If some key includes "/"-characters, + # you must provide an array of keys instead. + # + # Blacklist.filter( + # { "filesystem" => { + # "/dev/disk" => { + # "size" => "10mb" + # }, + # "map - autohome" => { + # "size" => "10mb" + # } + # }, + # "network" => { + # "interfaces" => { + # "eth0" => {...}, + # "eth1" => {...} + # } + # } + # }, + # ["network/interfaces/eth0", ["filesystem", "/dev/disk"]]) + # will exclude the eth0 and /dev/disk subtrees. + def self.filter(data, blacklist = nil) + return data if blacklist.nil? + + blacklist.each do |item| + Chef::Log.warn("Removing item #{item}") + remove_data(data, item) + end + data + end + + # Walk the data according to the keys provided by the blacklisted item + # and add the data to the whitelisting result. + def self.remove_data(data, item) + parts = to_array(item) + + item_ref = data + parts[0..-2].each do |part| + unless item_ref[part] + Chef::Log.warn("Could not find blacklist attribute #{item}.") + return nil + end + + item_ref = item_ref[part] + end + + unless item_ref.key?(parts[-1]) + Chef::Log.warn("Could not find blacklist attribute #{item}.") + return nil + end + + item_ref[parts[-1]] = nil + data + end + + private_class_method :remove_data + + # Accepts a String or an Array, and returns an Array of String keys that + # are used to traverse the data hash. Strings are split on "/", Arrays are + # assumed to contain exact keys (that is, Array elements will not be split + # by "/"). + def self.to_array(item) + return item if item.kind_of? Array + + parts = item.split("/") + parts.shift if !parts.empty? && parts[0].empty? + parts + end + + private_class_method :to_array + + end +end diff --git a/lib/chef/node.rb b/lib/chef/node.rb index 66527a2a37..1c2fbfee0f 100644 --- a/lib/chef/node.rb +++ b/lib/chef/node.rb @@ -34,6 +34,7 @@ require "chef/mash" require "chef/json_compat" require "chef/search/query" require "chef/whitelist" +require "chef/blacklist" class Chef class Node @@ -664,6 +665,13 @@ class Chef Chef::Log.info("Whitelisting #{level} node attributes for save.") data[level] = Chef::Whitelist.filter(data[level], whitelist) end + + blacklist_config_option = "#{level}_attribute_blacklist".to_sym + blacklist = Chef::Config[blacklist_config_option] + unless blacklist.nil? # nil => remove nothing + Chef::Log.info("Blacklisting #{level} node attributes for save") + data[level] = Chef::Blacklist.filter(data[level], blacklist) + end end data end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index 7dc7dd2380..01100c914d 100644 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -1609,6 +1609,81 @@ describe Chef::Node do end end + context "with blacklisted attributes configured" do + it "should only save non-blacklisted attributes (and subattributes)" do + Chef::Config[:automatic_attribute_blacklist] = [ + ["filesystem", "/dev/disk0s2"], + "network/interfaces/eth0", + ] + + data = { + "automatic" => { + "filesystem" => { + "/dev/disk0s2" => { "size" => "10mb" }, + "map - autohome" => { "size" => "10mb" }, + }, + "network" => { + "interfaces" => { + "eth0" => {}, + "eth1" => {}, + }, + }, + }, + "default" => {}, "normal" => {}, "override" => {} + } + + selected_data = { + "automatic" => { + "filesystem" => { + "/dev/disk0s2" => nil, + "map - autohome" => { "size" => "10mb" }, + }, + "network" => { + "interfaces" => { + "eth0" => nil, + "eth1" => {}, + }, + }, + }, + "default" => {}, "normal" => {}, "override" => {} + } + node.name("picky-monkey") + allow(node).to receive(:for_json).and_return(data) + expect(@rest).to receive(:put).with("nodes/picky-monkey", selected_data).and_return("foo") + node.save + end + + it "should save all attributes if the blacklist is empty" do + Chef::Config[:automatic_attribute_blacklist] = [] + + data = { + "automatic" => { + "filesystem" => { + "/dev/disk0s2" => { "size" => "10mb" }, + "map - autohome" => { "size" => "10mb" }, + }, + }, + "default" => {}, "normal" => {}, "override" => {} + } + + selected_data = { + "automatic" => { + "filesystem" => { + "/dev/disk0s2" => { "size" => "10mb" }, + "map - autohome" => { "size" => "10mb" }, + }, + }, + "default" => {}, "normal" => {}, "override" => {} + } + + node.name("picky-monkey") + allow(node).to receive(:for_json).and_return(data) + expect(@rest).to receive(:put).with("nodes/picky-monkey", selected_data).and_return("foo") + node.save + end + end + + context "when policyfile attributes are present" do before do |