class Chef::Resource::ChefDataBagItem

Attributes

raw_data_modifiers[R]

value 'ip_address', '127.0.0.1' value [ 'pushy', 'port' ], '9000' value 'ip_addresses' do |existing_value|

(existing_value || []) + [ '127.0.0.1' ]

end value 'ip_address', :delete

Public Class Methods

new(*args) click to toggle source
Calls superclass method Cheffish::BaseProperties::new
# File lib/chef/resource/chef_data_bag_item.rb, line 12
def initialize(*args)
  super
  if !property_is_set?(:data_bag) && run_context.cheffish.current_data_bag
    data_bag run_context.cheffish.current_data_bag
  end
  encryption = run_context.cheffish.current_data_bag_item_encryption
  if encryption
    encrypt true if encryption[:encrypt_all]
    secret encryption[:secret] if encryption[:secret]
    secret_path encryption[:secret_path] || run_context.config[:encrypted_data_bag_secret] if encryption[:secret_path] || run_context.config[:encrypted_data_bag_secret]
    encryption_cipher encryption[:encryption_cipher] if encryption[:encryption_cipher]
    encryption_version encryption[:encryption_version] || run_context.config[:data_bag_encrypt_version] if encryption[:encryption_version] || run_context.config[:data_bag_encrypt_version]
    old_secret encryption[:old_secret] if encryption[:old_secret]
    old_secret_path encryption[:old_secret_path] if encryption[:old_secret_path]
  end
end

Public Instance Methods

calculate_differences() click to toggle source

Figure out the differences between new and current

# File lib/chef/resource/chef_data_bag_item.rb, line 249
def calculate_differences
  if new_encrypt
    if current_resource.encrypt
      # Both are encrypted, check if the encryption type is the same
      description = ""
      if new_secret != current_resource.secret
        description << " with new secret"
      end
      if new_resource.encryption_version != current_resource.encryption_version
        description << " from v#{current_resource.encryption_version} to v#{new_resource.encryption_version} encryption"
      end

      if description != ""
        # Encryption is different, we're reencrypting
        differences = [ "re-encrypt#{description}"]
      else
        # Encryption is the same, we're just updating
        differences = []
      end
    else
      # New stuff should be encrypted, old is not.  Encrypting.
      differences = [ "encrypt with v#{new_resource.encryption_version} encryption" ]
    end

    # Get differences in the actual json
    if current_resource.secret
      json_differences(current_decrypted, new_decrypted, false, "", differences)
    elsif current_resource.encrypt
      # Encryption is different and we can't read the old values.  Only allow the change
      # if we're overwriting the data bag item
      unless new_resource.complete
        raise "Cannot encrypt #{new_resource.name} due to failure to decrypt existing resource.  Set 'complete true' to overwrite or add the old secret as old_secret / old_secret_path."
      end

      _differences = [ "overwrite data bag item (cannot decrypt old data bag item)"]
      differences = (new_resource.raw_data.keys & current_resource.raw_data.keys).map { |key| "overwrite #{key}" }
      differences += (new_resource.raw_data.keys - current_resource.raw_data.keys).map { |key| "add #{key}" }
      differences += (current_resource.raw_data.keys - new_resource.raw_data.keys).map { |key| "remove #{key}" }
    else
      json_differences(current_decrypted, new_decrypted, false, "", differences)
    end
  else
    if current_resource.encrypt
      # New stuff should not be encrypted, old is.  Decrypting.
      differences = [ "decrypt data bag item to plaintext" ]
    else
      differences = []
    end
    json_differences(current_decrypted, new_decrypted, true, "", differences)
  end
  differences
end
current_decrypted() click to toggle source

Get the current json decrypted, for comparison purposes

# File lib/chef/resource/chef_data_bag_item.rb, line 238
def current_decrypted
  @current_decrypted ||= if current_resource.secret
                           decrypt(current_resource.raw_data || { "id" => new_resource.id }, current_resource.secret)
                         elsif current_resource.encrypt
                           raise "Could not decrypt current data bag item #{current_resource.name}"
                         else
                           current_resource.raw_data || { "id" => new_resource.id }
                         end
end
data_handler() click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 310
def data_handler
  Chef::ChefFS::DataHandler::DataBagItemDataHandler.new
end
decrypt(json, secret) click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 210
def decrypt(json, secret)
  Chef::EncryptedDataBagItem.new(json, secret).to_hash
end
encrypt(json, secret, version) click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 214
def encrypt(json, secret, version)
  old_version = run_context.config[:data_bag_encrypt_version]
  run_context.config[:data_bag_encrypt_version] = version
  begin
    Chef::EncryptedDataBagItem.encrypt_data_bag_item(json, secret)
  ensure
    run_context.config[:data_bag_encrypt_version] = old_version
  end
end
fake_entry() click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 328
def fake_entry
  FakeEntry.new("#{new_resource.id}.json", FakeEntry.new(new_resource.data_bag))
end
keys() click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 314
def keys
  {
    "id" => :id,
    "data_bag" => :data_bag,
    "raw_data" => :raw_data,
  }
end
load_current_resource() click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 100
def load_current_resource
  begin
    json = rest.get("data/#{new_resource.data_bag}/#{new_resource.id}")
    resource = Chef::Resource::ChefDataBagItem.new(new_resource.name, run_context)
    resource.raw_data json
    @current_resource = resource
  rescue Net::HTTPClientException => e
    if e.response.code == "404"
      @current_resource = not_found_resource
    else
      raise
    end
  end

  # Determine if data bag is encrypted and if so, what its version is
  _first_real_key, first_real_value = (current_resource.raw_data || {}).find { |key, value| key != "id" && !value.nil? }
  if first_real_value
    if first_real_value.is_a?(Hash) &&
        first_real_value["version"].is_a?(Integer) &&
        first_real_value["version"] > 0 &&
        first_real_value.key?("encrypted_data")

      current_resource.encrypt true
      current_resource.encryption_version first_real_value["version"]

      decrypt_error = nil

      # Check if the desired secret is the one (which it generally should be)

      if new_resource.secret || new_resource.secret_path
        begin
          Chef::EncryptedDataBagItem::Decryptor.for(first_real_value, new_secret).for_decrypted_item
          current_resource.secret new_secret
        rescue Chef::EncryptedDataBagItem::DecryptionFailure
          decrypt_error = $!
        end
      end

      # If the current secret doesn't work, look through the specified old secrets

      unless current_resource.secret
        old_secrets = []
        if new_resource.old_secret
          old_secrets += Array(new_resource.old_secret)
        end
        if new_resource.old_secret_path
          old_secrets += Array(new_resource.old_secret_path).map do |secret_path|
            Chef::EncryptedDataBagItem.load_secret(new_resource.old_secret_file)
          end
        end
        old_secrets.each do |secret|

          Chef::EncryptedDataBagItem::Decryptor.for(first_real_value, secret).for_decrypted_item
          current_resource.secret secret
        rescue Chef::EncryptedDataBagItem::DecryptionFailure
          decrypt_error = $!

        end

        # If we couldn't figure out the secret, emit a warning (this isn't a fatal flaw unless we
        # need to reuse one of the values from the data bag)
        unless current_resource.secret
          if decrypt_error
            Chef::Log.warn "Existing data bag is encrypted, but could not decrypt: #{decrypt_error.message}."
          else
            Chef::Log.warn "Existing data bag is encrypted, but no secret was specified."
          end
        end
      end
    end
  else

    # There are no encryptable values, so pretend encryption is the same as desired

    current_resource.encrypt(new_resource.encrypt) unless new_resource.encrypt.nil?
    current_resource.encryption_version(new_resource.encryption_version) if new_resource.encryption_version
    if new_resource.secret || new_resource.secret_path
      current_resource.secret new_secret
    end
  end
end
new_decrypted() click to toggle source

Get the desired (new) json pre-encryption, for comparison purposes

# File lib/chef/resource/chef_data_bag_item.rb, line 225
def new_decrypted
  @new_decrypted ||= begin
    if new_resource.complete
      result = new_resource.raw_data || {}
    else
      result = current_decrypted.merge(new_resource.raw_data || {})
    end
    result["id"] = new_resource.id
    _result = apply_modifiers(new_resource.raw_data_modifiers, result)
  end
end
new_encrypt() click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 194
def new_encrypt
  new_resource.encrypt.nil? ? current_resource.encrypt : new_resource.encrypt
end
new_json() click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 182
def new_json
  @new_json ||= begin
    if new_encrypt
      # Encrypt new stuff
      result = encrypt(new_decrypted, new_secret, new_resource.encryption_version)
    else
      result = new_decrypted
    end
    result
  end
end
new_secret() click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 198
def new_secret
  @new_secret ||= if new_resource.secret
                    new_resource.secret
                  elsif new_resource.secret_path
                    Chef::EncryptedDataBagItem.load_secret(new_resource.secret_path)
                  elsif new_resource.encrypt.nil?
                    current_resource.secret
                  else
                    raise "Data bag item #{new_resource.name} has encryption on but no secret or secret_path is specified"
                  end
end
not_found_resource() click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 322
def not_found_resource
  resource = super
  resource.data_bag new_resource.data_bag
  resource
end
resource_class() click to toggle source

Helpers

# File lib/chef/resource/chef_data_bag_item.rb, line 306
def resource_class
  Chef::Resource::ChefDataBagItem
end
value(raw_data_path, value = NOT_PASSED, &block) click to toggle source
# File lib/chef/resource/chef_data_bag_item.rb, line 62
def value(raw_data_path, value = NOT_PASSED, &block)
  @raw_data_modifiers ||= []
  if value != NOT_PASSED
    @raw_data_modifiers << [ raw_data_path, value ]
  elsif block
    @raw_data_modifiers << [ raw_data_path, block ]
  else
    raise "value requires either a value or a block"
  end
end