Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notification after Tower credential CUD operations #14625

Merged
merged 7 commits into from
Apr 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -139,3 +139,13 @@
:expires_in: 24.hours
:level: :error
:audience: global
- :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