Skip to content

Commit

Permalink
Merge 72b7ec5 into 8e109d6
Browse files Browse the repository at this point in the history
  • Loading branch information
Fryguy committed Jun 22, 2018
2 parents 8e109d6 + 72b7ec5 commit 69ef223
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 95 deletions.
59 changes: 20 additions & 39 deletions lib/vmdb/settings.rb
Expand Up @@ -7,7 +7,6 @@
module Vmdb
class Settings
extend Vmdb::SettingsWalker::ClassMethods
include Vmdb::Logging

class ConfigurationInvalid < StandardError
attr_accessor :errors
Expand All @@ -22,10 +21,9 @@ def initialize(errors)
PASSWORD_FIELDS = Vmdb::SettingsWalker::PASSWORD_FIELDS
DUMP_LOG_FILE = Rails.root.join("log/last_settings.txt").freeze

# RESET_COMMAND remove record for selected key and specific resource
# RESET_ALL_COMMAND remove all records for selected key and all resources
MAGIC_VALUES = [RESET_COMMAND = "<<reset>>".freeze,
RESET_ALL_COMMAND = "<<reset_all>>".freeze].freeze
# Magic value to reset a resource's setting to the parent's value
RESET_COMMAND = "<<reset>>".freeze
RESET_VALUE = HashDiffer::MissingKey

cattr_accessor :last_loaded

Expand Down Expand Up @@ -67,14 +65,14 @@ def self.valid?
end

def self.save!(resource, hash)
new_settings = build_without_local(resource).load!.merge!(hash).to_hash
settings_to_validate = remove_magic_values(new_settings.deep_clone)
new_settings = build_without_local(resource).load!.merge!(hash.deep_symbolize_keys).to_hash
replace_magic_values!(new_settings, resource)

valid, errors = validate(settings_to_validate)
valid, errors = validate(new_settings)
raise ConfigurationInvalid.new(errors) unless valid # rubocop:disable Style/RaiseArgs

hash_for_parent = parent_settings_without_local(resource).load!.to_hash
diff = HashDiffer.diff(hash_for_parent, new_settings)
parent_settings = parent_settings_without_local(resource).load!.to_hash
diff = HashDiffer.diff(parent_settings, new_settings)
encrypt_passwords!(diff)
deltas = HashDiffer.diff_to_deltas(diff)
apply_settings_changes(resource, deltas)
Expand Down Expand Up @@ -189,13 +187,24 @@ def self.reset_settings_constant(settings)
end
private_class_method :reset_settings_constant

def self.replace_magic_values!(settings, resource)
parent_settings = nil

walk(settings) do |key, value, path, owner|
next unless value == RESET_COMMAND

parent_settings ||= parent_settings_without_local(resource).load!.to_hash
owner[key] = parent_settings.key_path?(path) ? parent_settings.fetch_path(path) : RESET_VALUE
end
end
private_class_method :replace_magic_values!

def self.apply_settings_changes(resource, deltas)
resource.transaction do
index = resource.settings_changes.index_by(&:key)

deltas.each do |delta|
record = index.delete(delta[:key])
next if process_magic_value(resource, delta)
if record
record.update_attributes!(delta)
else
Expand All @@ -206,33 +215,5 @@ def self.apply_settings_changes(resource, deltas)
end
end
private_class_method :apply_settings_changes

def self.process_magic_value(resource, delta)
return false unless MAGIC_VALUES.include?(delta[:value])
case delta[:value]
when RESET_COMMAND
_log.info("resetting #{delta[:key]} on #{resource.class} id:#{resource.id}")
resource.settings_changes.where("key LIKE ?", "#{delta[:key]}%").destroy_all
when RESET_ALL_COMMAND
_log.info("resetting #{delta[:key]} to default for all resorces")
SettingsChange.where("key LIKE ?", "#{delta[:key]}%").destroy_all
end
true
end
private_class_method :process_magic_value

def self.walk_hash(hash, &block)
hash.each do |key, value|
yield key, value, hash
walk_hash(value, &block) if value&.class == Hash
end
end
private_class_method :walk_hash

def self.remove_magic_values(hash)
walk_hash(hash) { |key, value, owner| owner.delete(key) if MAGIC_VALUES.include?(value) }
hash
end
private_class_method :remove_magic_values
end
end
127 changes: 71 additions & 56 deletions spec/lib/vmdb/settings_spec.rb
Expand Up @@ -266,82 +266,97 @@
expect(miq_server.zone.settings_changes.count).to eq 0
end

context "deleting entries" do
let(:server_value) { 1 }
let(:zone_value) { 2 }
describe described_class::RESET_COMMAND do
let(:server_value) { "server" }
let(:zone_value) { "zone" }
let(:region_value) { "region" }
let(:server_array_value) { [{:key1 => server_value}, {:key1 => server_value}] }
let(:zone_array_value) { [{:key1 => zone_value}, {:key1 => zone_value}] }

let(:reset) { ::Vmdb::Settings::RESET_COMMAND }
let(:reset_all) { ::Vmdb::Settings::RESET_ALL_COMMAND }

let(:second_server) { FactoryGirl.create(:miq_server, :zone => miq_server.zone) }
let(:reset) { described_class::RESET_COMMAND }

before do
MiqRegion.seed

described_class.save!(miq_server, :api => {:token_ttl => server_value}, :session => {:timeout => server_value})
described_class.save!(miq_server, :api => {:new_key => "new value"})
described_class.save!(second_server, :api => {:token_ttl => server_value}, :session => {:timeout => server_value})
described_class.save!(
MiqRegion.first,
:api => {
:token_ttl => region_value,
:authentication_timeout => region_value
},
:session => {:timeout => 2}
)
described_class.save!(
miq_server.zone,
:api => {
:token_ttl => zone_value
},
:array => zone_array_value
)
described_class.save!(
miq_server,
:api => {
:token_ttl => server_value,
:authentication_timeout => server_value,
:new_key => "new value"
},
:session => {:timeout => 1},
:array => server_array_value
)
end

it "inherits a leaf-level value from the parent" do
described_class.save!(miq_server, :api => {:token_ttl => reset})

described_class.save!(miq_server.zone, :api => {:token_ttl => zone_value}, :session => {:timeout => zone_value})
described_class.save!(miq_server, :array => [:element1 => 1, :element2 => 2])
expect(SettingsChange.count).to eq 8
expect(described_class.for_resource(miq_server).api.token_ttl).to eq zone_value
end

context "magic value <<reset>>" do
it "deletes key-value for specific key for the resource if specified on leaf level" do
described_class.save!(miq_server, :api => {:token_ttl => reset})
it "inherits a node-level value from the parent" do
described_class.save!(miq_server, :api => reset)

expect(SettingsChange.count).to eq 7
expect(miq_server.settings_changes.find_by(:key => "/api/token_ttl")).to be nil
expect(Vmdb::Settings.for_resource(miq_server).api.new_key).to eq "new value"
expect(Vmdb::Settings.for_resource(second_server).api.token_ttl).to eq server_value
end
expect(described_class.for_resource(miq_server).api.token_ttl).to eq zone_value
expect(described_class.for_resource(miq_server).api.authentication_timeout).to eq region_value
end

it "deletes current node and all sub-nodes for the resource if specified on node level" do
described_class.save!(miq_server, :api => reset)
it "inherits an array from the parent" do
described_class.save!(miq_server, :array => reset)

expect(SettingsChange.count).to eq 6
expect(miq_server.settings_changes.where("key LIKE ?", "/api%").count).to eq 0
expect(Vmdb::Settings.for_resource(miq_server).api.new_key).to eq nil
expect(Vmdb::Settings.for_resource(second_server).api.token_ttl).to eq server_value
end
expect(described_class.for_resource(miq_server).to_hash[:array]).to eq zone_array_value
end

it "deletes new key-value settings not present in defaul yaml" do
described_class.save!(miq_server, :api => {:new_key => reset})
expect(Vmdb::Settings.for_resource(miq_server).api.new_key).to eq nil
end
it "deletes a leaf-level value not present in the parent" do
described_class.save!(miq_server, :api => {:new_key => reset})

it "deletes array" do
described_class.save!(miq_server, :array => reset)
expect(Vmdb::Settings.for_resource(miq_server).array).to be nil
end
expect(described_class.for_resource(miq_server).api.new_key).to be_nil
expect(miq_server.reload.settings_changes.where(:key => "/api/new_key")).to_not exist
end

it "passes validation" do
described_class.save!(miq_server, :session => {:timeout => reset})
expect(Vmdb::Settings.for_resource(miq_server).session.timeout).to eq zone_value
end
it "deletes a leaf-level value not present in the parent when reset at the node level" do
described_class.save!(miq_server, :api => reset)

expect(described_class.for_resource(miq_server).api.new_key).to be_nil
expect(described_class.for_resource(miq_server).api.token_ttl).to eq zone_value
expect(miq_server.reload.settings_changes.where(:key => "/api/new_key")).to_not exist
end

context "magic value <<reset_all>>" do
it "deletes all key-value for specific key for all resource if specified on leaf levelher" do
described_class.save!(miq_server, :api => {:token_ttl => reset_all})
it "deletes an array value not present in the parent" do
described_class.save!(miq_server.zone, :array => reset)

expect(SettingsChange.where("key LIKE ?", "/api/token_ttl").count).to eq 0
expect(SettingsChange.where("key LIKE ?", "/api%").count).to eq 1
expect(SettingsChange.where("key LIKE ?", "/session%").count).to eq 3
end
expect(described_class.for_resource(miq_server.zone).array).to be_nil
expect(miq_server.zone.reload.settings_changes.where(:key => "/array")).to_not exist
end

it "deletes specific node and all sub-nodes for all resources if specified on node level" do
described_class.save!(miq_server, :api => reset_all)
it "at a parent level does not push down changes to children" do
described_class.save!(miq_server.zone, :api => {:token_ttl => reset})

expect(SettingsChange.where("key LIKE ?", "/api%").count).to eq 0
expect(SettingsChange.where("key LIKE ?", "/session%").count).to eq 3
end
expect(described_class.for_resource(miq_server.zone).api.token_ttl).to eq region_value
expect(described_class.for_resource(miq_server).api.token_ttl).to eq server_value
end

it "passes validation" do
described_class.save!(miq_server, :session => {:timeout => reset_all})
expect(SettingsChange.where("key LIKE ?", "/session%").count).to eq 0
end
it "passes validation" do
described_class.save!(miq_server, :session => {:timeout => reset})

expect(described_class.for_resource(miq_server).session.timeout).to eq 2
end
end
end
Expand Down

0 comments on commit 69ef223

Please sign in to comment.