summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mullins <rmullins@secureworks.com>2017-03-03 14:39:07 -0500
committerRobert Mullins <rmullins@secureworks.com>2017-03-08 11:41:56 -0500
commit848fbf3c081dabe4a7f18a42597b1a68f68b6009 (patch)
treec7d49291f920dbde14b43c70953b3c1f9bc800b1
parent5dc375d6dd7acb1d32f06b3ee663201d0a119ccf (diff)
downloadchef-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.rb9
-rw-r--r--lib/chef/blacklist.rb81
-rw-r--r--lib/chef/node.rb8
-rw-r--r--spec/unit/node_spec.rb75
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