Skip to content

Commit

Permalink
Merge pull request #14625 from jameswnl/notification
Browse files Browse the repository at this point in the history
Notification after Tower credential CUD operations
(cherry picked from commit 4f7363a)
  • Loading branch information
bdunne authored and simaishi committed Apr 11, 2017
1 parent 28ebe8b commit 3bdf50f
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ def provider_collection(manager)

def provider_params(params)
params[:username] = params.delete(:userid) if params.include?(:userid)
params[:username] = params.delete('userid') if params.include?('userid')
params[:kind] = self::TOWER_KIND
params
end

def hide_secrets(params)
params.each_with_object({}) do |attr, h|
h[attr.first] = self::API_ATTRIBUTES[attr.first] && self::API_ATTRIBUTES[attr.first][:type] == :password ? '******' : attr.second
end
end
end

def provider_object(connection = nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,36 @@ module ManageIQ::Providers::AnsibleTower::Shared::AutomationManager::TowerApi

module ClassMethods
def create_in_provider(manager_id, params)
params = provider_params(params) if respond_to? :provider_params
params = provider_params(params) if respond_to?(:provider_params)
manager = ExtManagementSystem.find(manager_id)
tower_object = provider_collection(manager).create!(params)

refresh(manager)
find_by!(:manager_id => manager.id, :manager_ref => tower_object.id)
rescue AnsibleTowerClient::ClientError, ActiveRecord::RecordNotFound => error
raise
ensure
notify('create_in_provider', manager.id, params, error.nil?)
end

def create_in_provider_queue(manager_id, params)
manager = ExtManagementSystem.find(manager_id)
action = "Creating #{name} with name=#{params[:name] || params['name']}"
action = "Creating #{name} with name=#{params[:name]}"
queue(manager.my_zone, nil, "create_in_provider", [manager_id, params], action)
end

private
def notify(op, manager_id, params, success)
params = hide_secrets(params) if respond_to?(:hide_secrets)
Notification.create(
:type => success ? :tower_op_success : :tower_op_failure,
:options => {
:op_name => "#{name.demodulize} #{op}",
:op_arg => params.to_s,
:tower => "Tower(manager_id: #{manager_id})"
}
)
end

def refresh(manager)
# Get the record in our database
Expand Down Expand Up @@ -47,12 +62,16 @@ def queue(zone, instance_id, method_name, args, action)

def update_in_provider(params)
params.delete(:task_id) # in case this is being called through update_in_provider_queue which will stick in a :task_id
params = self.class.provider_params(params) if self.class.respond_to? :provider_params
params = self.class.provider_params(params) if self.class.respond_to?(:provider_params)
with_provider_object do |provider_object|
provider_object.update_attributes!(params)
end
self.class.send('refresh', manager)
reload
rescue AnsibleTowerClient::ClientError => error
raise
ensure
self.class.send('notify', 'update_in_provider', manager.id, params, error.nil?)
end

def update_in_provider_queue(params)
Expand All @@ -63,6 +82,10 @@ def update_in_provider_queue(params)
def delete_in_provider
with_provider_object(&:destroy!)
self.class.send('refresh', manager)
rescue AnsibleTowerClient::ClientError => error
raise
ensure
self.class.send('notify', 'delete_in_provider', manager.id, {:manager_ref => manager_ref}, error.nil?)
end

def delete_in_provider_queue
Expand Down
10 changes: 10 additions & 0 deletions db/fixtures/notification_types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,13 @@
:expires_in: 24.hours
:level: :info
:audience: superadmin
- :name: tower_op_success
:message: 'The operation %{op_name} %{op_arg} on %{tower} completed successfully.'
:expires_in: 24.hours
:level: :success
:audience: global
- :name: tower_op_failure
:message: 'The operation %{op_name} %{op_arg} on %{tower} has failed to complete. Please check the logs for further details.'
:expires_in: 24.hours
:level: :success
:audience: global
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,33 @@
}
end

it ".create_in_provider" do
let(:expected_notify) do
{
:type => :tower_op_success,
:options => {
:op_name => "#{described_class.name.demodulize} create_in_provider",
:op_arg => params.to_s,
:tower => "Tower(manager_id: #{manager.id})"
}
}
end

it ".create_in_provider to succeed and send notification" do
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
store_new_project(project, manager)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager)

expect(projects).to receive(:create!).with(params)
expect(Notification).to receive(:create).with(expected_notify)
expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class)
end

it "not found during refresh" do
it ".create_in_provider to fail(not found during refresh) and send notification" do
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager)

expected_notify[:type] = :tower_op_failure
expect(Notification).to receive(:create).with(expected_notify)
expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound)
end

Expand Down Expand Up @@ -68,15 +81,34 @@ def store_new_project(project, manager)

context "Delete through API" do
let(:projects) { double("AnsibleTowerClient::Collection", :find => tower_project) }
let(:tower_project) { double("AnsibleTowerClient::Project", :destroy! => nil, :id => 1) }
let(:tower_project) { double("AnsibleTowerClient::Project", :destroy! => nil, :id => '1') }
let(:project) { described_class.create!(:manager => manager, :manager_ref => tower_project.id) }
let(:expected_notify) do
{
:type => :tower_op_success,
:options => {
:op_name => "#{described_class.name.demodulize} delete_in_provider",
:op_arg => {:manager_ref => tower_project.id}.to_s,
:tower => "Tower(manager_id: #{manager.id})"
}
}
end

it "#delete_in_provider" do
it "#delete_in_provider to succeed and send notification" do
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(Notification).to receive(:create).with(expected_notify)
project.delete_in_provider
end

it "#delete_in_provider to fail (find the credential) and send notification" do
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
allow(projects).to receive(:find).and_raise(AnsibleTowerClient::ClientError)
expected_notify[:type] = :tower_op_failure
expect(Notification).to receive(:create).with(expected_notify)
expect { project.delete_in_provider }.to raise_error(AnsibleTowerClient::ClientError)
end

it "#delete_in_provider_queue" do
task_id = project.delete_in_provider_queue
expect(MiqTask.find(task_id)).to have_attributes(:name => "Deleting #{described_class.name} with Tower internal reference=#{project.manager_ref}")
Expand All @@ -96,13 +128,32 @@ def store_new_project(project, manager)
let(:projects) { double("AnsibleTowerClient::Collection", :find => tower_project) }
let(:tower_project) { double("AnsibleTowerClient::Project", :update_attributes! => {}, :id => 1) }
let(:project) { described_class.create!(:manager => manager, :manager_ref => tower_project.id) }
let(:expected_notify) do
{
:type => :tower_op_success,
:options => {
:op_name => "#{described_class.name.demodulize} update_in_provider",
:op_arg => {}.to_s,
:tower => "Tower(manager_id: #{manager.id})"
}
}
end

it "#update_in_provider" do
it "#update_in_provider to succeed and send notification" do
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(Notification).to receive(:create).with(expected_notify)
expect(project.update_in_provider({})).to be_a(described_class)
end

it "#update_in_provider to fail (at update_attributes!) and send notification" do
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
expect(tower_project).to receive(:update_attributes!).with({}).and_raise(AnsibleTowerClient::ClientError)
expected_notify[:type] = :tower_op_failure
expect(Notification).to receive(:create).with(expected_notify)
expect { project.update_in_provider({}) }.to raise_error(AnsibleTowerClient::ClientError)
end

it "#update_in_provider_queue" do
task_id = project.update_in_provider_queue({})
expect(MiqTask.find(task_id)).to have_attributes(:name => "Updating #{described_class.name} with Tower internal reference=#{project.manager_ref}")
Expand Down
102 changes: 81 additions & 21 deletions spec/support/ansible_shared/automation_manager/credential.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
let(:api) { double("AnsibleTowerClient::Api", :credentials => credentials) }

context "Create through API" do
let(:credentials) { double("AnsibleTowerClient::Collection", :create! => credential) }
let(:credential) { AnsibleTowerClient::Credential.new(nil, credential_json) }

let(:credentials) { double("AnsibleTowerClient::Collection", :create! => credential) }
let(:credential) { AnsibleTowerClient::Credential.new(nil, credential_json) }
let(:credential_json) do
params.merge(
:id => 10,
).stringify_keys.to_json
end

let(:params) do
{
:description => "Description",
Expand All @@ -23,29 +21,54 @@
:userid => 'john'
}
end

it ".create_in_provider" do
expected_params = {
let(:expected_params) do
{
:description => "Description",
:name => "My Credential",
:related => {},
:username => "john",
:kind => described_class::TOWER_KIND
}
end
let(:expected_notify_params) do
{
:description => "Description",
:name => "My Credential",
:related => {},
:username => "john",
:kind => described_class::TOWER_KIND
}
end
let(:expected_notify) do
{
:type => :tower_op_success,
:options => {
:op_name => "#{described_class.name.demodulize} create_in_provider",
:op_arg => expected_params.to_s,
:tower => "Tower(manager_id: #{manager.id})"
}
}
end

it ".create_in_provider to succeed and send notification" do
expected_params[:organization] = 1 if described_class.name.include?("::EmbeddedAnsible::")
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
store_new_credential(credential, manager)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager)
expect(credentials).to receive(:create!).with(expected_params)
expect(Notification).to receive(:create).with(expected_notify)
expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class)
end

it "not found during refresh" do
it ".create_in_provider to fail (not found during refresh) and send notification" do
expected_params[:organization] = 1 if described_class.name.include?("::EmbeddedAnsible::")
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager)

expect(credentials).to receive(:create!).with(expected_params)
expected_notify[:type] = :tower_op_failure
expect(Notification).to receive(:create).with(expected_notify).and_return(double(Notification))
expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound)
end

Expand Down Expand Up @@ -73,15 +96,34 @@ def store_new_credential(credential, manager)

context "Delete through API" do
let(:credentials) { double("AnsibleTowerClient::Collection", :find => credential) }
let(:credential) { double("AnsibleTowerClient::Credential", :destroy! => nil, :id => 1) }
let(:credential) { double("AnsibleTowerClient::Credential", :destroy! => nil, :id => '1') }
let(:ansible_cred) { described_class.create!(:resource => manager, :manager_ref => credential.id) }
let(:expected_notify) do
{
:type => :tower_op_success,
:options => {
:op_name => "#{described_class.name.demodulize} delete_in_provider",
:op_arg => {:manager_ref => credential.id}.to_s,
:tower => "Tower(manager_id: #{manager.id})"
}
}
end

it "#delete_in_provider" do
it "#delete_in_provider to succeed and send notification" do
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(Notification).to receive(:create).with(expected_notify)
ansible_cred.delete_in_provider
end

it "#delete_in_provider to fail (finding credential) and send notification" do
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
allow(credentials).to receive(:find).and_raise(AnsibleTowerClient::ClientError)
expected_notify[:type] = :tower_op_failure
expect(Notification).to receive(:create).with(expected_notify)
expect { ansible_cred.delete_in_provider }.to raise_error(AnsibleTowerClient::ClientError)
end

it "#delete_in_provider_queue" do
task_id = ansible_cred.delete_in_provider_queue
expect(MiqTask.find(task_id)).to have_attributes(:name => "Deleting #{described_class.name} with Tower internal reference=#{ansible_cred.manager_ref}")
Expand All @@ -98,20 +140,38 @@ def store_new_credential(credential, manager)
end

context "Update through API" do
let(:credentials) { double("AnsibleTowerClient::Collection", :find => credential) }
let(:credential) { double("AnsibleTowerClient::Credential", :update_attributes! => {}, :id => 1) }
let(:ansible_cred) { described_class.create!(:resource => manager, :manager_ref => credential.id) }

it "#update_in_provider" do
expected_params = {
:username => 'john',
:kind => described_class::TOWER_KIND
let(:credentials) { double("AnsibleTowerClient::Collection", :find => credential) }
let(:credential) { double("AnsibleTowerClient::Credential", :id => 1) }
let(:ansible_cred) { described_class.create!(:resource => manager, :manager_ref => credential.id) }
let(:params) { {:userid => 'john'} }
let(:expected_params) { {:username => 'john', :kind => described_class::TOWER_KIND} }
let(:expected_notify) do
{
:type => :tower_op_success,
:options => {
:op_name => "#{described_class.name.demodulize} update_in_provider",
:op_arg => expected_params.to_s,
:tower => "Tower(manager_id: #{manager.id})"
}
}
end

it "#update_in_provider to succeed and send notification" do
expected_params[:organization] = 1 if described_class.name.include?("::EmbeddedAnsible::")
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(credential).to receive(:update_attributes!).with(expected_params)
expect(ansible_cred.update_in_provider('userid' => 'john')).to be_a(described_class)
expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task])
expect(Notification).to receive(:create).with(expected_notify)
expect(ansible_cred.update_in_provider(params)).to be_a(described_class)
end

it "#update_in_provider to fail (doing update_attributes!) and send notification" do
expected_params[:organization] = 1 if described_class.name.include?("::EmbeddedAnsible::")
expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc)
expect(credential).to receive(:update_attributes!).with(expected_params).and_raise(AnsibleTowerClient::ClientError)
expected_notify[:type] = :tower_op_failure
expect(Notification).to receive(:create).with(expected_notify).and_return(double(Notification))
expect { ansible_cred.update_in_provider(params) }.to raise_error(AnsibleTowerClient::ClientError)
end

it "#update_in_provider_queue" do
Expand Down

0 comments on commit 3bdf50f

Please sign in to comment.