From f2db149d6495c8dd8a74aa202e74a41a4411e6a8 Mon Sep 17 00:00:00 2001 From: Ondrej Prazak Date: Fri, 29 Jul 2016 17:06:19 +0200 Subject: [PATCH] Fixes #15979 - Add API for Ansible roles --- app/controllers/ansible_roles_controller.rb | 2 +- .../api/v2/ansible_roles_controller.rb | 52 +++++++++++++++++++ .../foreman_ansible/api_roles_importer.rb | 16 ++++++ .../foreman_ansible/roles_importer.rb | 20 +------ .../foreman_ansible/ui_roles_importer.rb | 26 ++++++++++ .../api/v2/ansible_roles/import.json.rabl | 3 ++ .../api/v2/ansible_roles/index.json.rabl | 3 ++ .../api/v2/ansible_roles/obsolete.json.rabl | 3 ++ app/views/api/v2/ansible_roles/show.json.rabl | 3 ++ config/routes.rb | 16 ++++++ lib/foreman_ansible/engine.rb | 6 ++- .../api/v2/ansible_roles_controller_test.rb | 24 +++++++++ test/unit/services/api_roles_importer_test.rb | 28 ++++++++++ test/unit/services/roles_importer_test.rb | 24 --------- test/unit/services/ui_roles_importer_test.rb | 31 +++++++++++ 15 files changed, 211 insertions(+), 46 deletions(-) create mode 100644 app/controllers/api/v2/ansible_roles_controller.rb create mode 100644 app/services/foreman_ansible/api_roles_importer.rb create mode 100644 app/services/foreman_ansible/ui_roles_importer.rb create mode 100644 app/views/api/v2/ansible_roles/import.json.rabl create mode 100644 app/views/api/v2/ansible_roles/index.json.rabl create mode 100644 app/views/api/v2/ansible_roles/obsolete.json.rabl create mode 100644 app/views/api/v2/ansible_roles/show.json.rabl create mode 100644 test/functional/api/v2/ansible_roles_controller_test.rb create mode 100644 test/unit/services/api_roles_importer_test.rb create mode 100644 test/unit/services/ui_roles_importer_test.rb diff --git a/app/controllers/ansible_roles_controller.rb b/app/controllers/ansible_roles_controller.rb index ad1de533f..0d14cfb92 100644 --- a/app/controllers/ansible_roles_controller.rb +++ b/app/controllers/ansible_roles_controller.rb @@ -47,6 +47,6 @@ def find_proxy end def create_importer - @importer = ForemanAnsible::RolesImporter.new(@proxy) + @importer = ForemanAnsible::UiRolesImporter.new(@proxy) end end diff --git a/app/controllers/api/v2/ansible_roles_controller.rb b/app/controllers/api/v2/ansible_roles_controller.rb new file mode 100644 index 000000000..790986ee5 --- /dev/null +++ b/app/controllers/api/v2/ansible_roles_controller.rb @@ -0,0 +1,52 @@ +module Api + module V2 + # API controller for Ansible Roles + class AnsibleRolesController < ::Api::V2::BaseController + include ::Api::Version2 + + before_action :find_resource, :only => [:show, :destroy] + before_action :find_proxy, :only => [:import, :obsolete] + before_action :create_importer, :only => [:import, :obsolete] + + api :GET, '/ansible/ansible_roles/:id', N_('Show role') + param :id, :identifier, :required => true + def show + end + + api :GET, '/ansible/ansible_roles', N_('List Ansible roles') + param_group :search_and_pagination, ::Api::V2::BaseController + def index + @ansible_roles = resource_scope_for_index + end + + api :DELETE, '/ansible/ansible_roles/:id', N_('Deletes Ansible role') + param :id, :identifier, :required => true + def destroy + process_response @ansible_role.destroy + end + + api :POST, '/ansible/ansible_roles/import', N_('Import Ansible roles') + param :proxy, Hash, N_('Smart Proxy to import from') + def import + @imported = @importer.import! + end + + api :POST, '/asnible/ansible_roles/obsolete', N_('Obsolete Ansible roles') + param :proxy, Hash, N_('Smart Proxy to import from') + def obsolete + @obsoleted = @importer.obsolete! + end + + private + + def find_proxy + return nil unless params[:proxy] + @proxy = SmartProxy.authorized(:view_smart_proxies).find(params[:proxy]) + end + + def create_importer + @importer = ForemanAnsible::ApiRolesImporter.new(@proxy) + end + end + end +end diff --git a/app/services/foreman_ansible/api_roles_importer.rb b/app/services/foreman_ansible/api_roles_importer.rb new file mode 100644 index 000000000..4f6d695cb --- /dev/null +++ b/app/services/foreman_ansible/api_roles_importer.rb @@ -0,0 +1,16 @@ +module ForemanAnsible + # imports Ansible roles through API + class ApiRolesImporter < RolesImporter + def import! + new_roles = import_role_names[:new] + new_roles.map(&:save) + new_roles + end + + def obsolete! + obsolete_roles = import_role_names[:obsolete] + obsolete_roles.map(&:destroy) + obsolete_roles + end + end +end diff --git a/app/services/foreman_ansible/roles_importer.rb b/app/services/foreman_ansible/roles_importer.rb index 5ce2127a7..d05d2d8e9 100644 --- a/app/services/foreman_ansible/roles_importer.rb +++ b/app/services/foreman_ansible/roles_importer.rb @@ -7,7 +7,7 @@ def initialize(proxy = nil) @ansible_proxy = proxy end - def import! + def import_role_names return import_roles remote_roles if ansible_proxy import_roles local_roles end @@ -19,24 +19,6 @@ def import_roles(roles) detect_changes imported end - def finish_import(changes) - return unless changes.present? - create_new_roles changes['new'] if changes['new'] - delete_old_roles changes['obsolete'] if changes['obsolete'] - end - - def create_new_roles(changes) - changes.values.each do |new_role| - ::AnsibleRole.create(JSON.parse(new_role)) - end - end - - def delete_old_roles(changes) - changes.values.each do |old_role| - ::AnsibleRole.find(JSON.parse(old_role)['id']).destroy - end - end - def detect_changes(imported) changes = {}.with_indifferent_access old, changes[:new] = imported.partition { |role| role.id.present? } diff --git a/app/services/foreman_ansible/ui_roles_importer.rb b/app/services/foreman_ansible/ui_roles_importer.rb new file mode 100644 index 000000000..ba8ebeb62 --- /dev/null +++ b/app/services/foreman_ansible/ui_roles_importer.rb @@ -0,0 +1,26 @@ +module ForemanAnsible + # imports ansible roles through UI + class UiRolesImporter < RolesImporter + def import! + import_role_names + end + + def finish_import(changes) + return unless changes.present? + create_new_roles changes['new'] if changes['new'] + delete_old_roles changes['obsolete'] if changes['obsolete'] + end + + def create_new_roles(changes) + changes.values.each do |new_role| + ::AnsibleRole.create(JSON.parse(new_role)) + end + end + + def delete_old_roles(changes) + changes.values.each do |old_role| + ::AnsibleRole.find(JSON.parse(old_role)['id']).destroy + end + end + end +end diff --git a/app/views/api/v2/ansible_roles/import.json.rabl b/app/views/api/v2/ansible_roles/import.json.rabl new file mode 100644 index 000000000..60e856abe --- /dev/null +++ b/app/views/api/v2/ansible_roles/import.json.rabl @@ -0,0 +1,3 @@ +collection @imported => :imported + +extends 'api/v2/ansible_roles/show' diff --git a/app/views/api/v2/ansible_roles/index.json.rabl b/app/views/api/v2/ansible_roles/index.json.rabl new file mode 100644 index 000000000..adf0b23d0 --- /dev/null +++ b/app/views/api/v2/ansible_roles/index.json.rabl @@ -0,0 +1,3 @@ +collection @ansible_roles + +extends 'api/v2/ansible_roles/show' diff --git a/app/views/api/v2/ansible_roles/obsolete.json.rabl b/app/views/api/v2/ansible_roles/obsolete.json.rabl new file mode 100644 index 000000000..e8008f768 --- /dev/null +++ b/app/views/api/v2/ansible_roles/obsolete.json.rabl @@ -0,0 +1,3 @@ +collection @obsoleted => :obsoleted + +extends 'api/v2/ansible_roles/show' diff --git a/app/views/api/v2/ansible_roles/show.json.rabl b/app/views/api/v2/ansible_roles/show.json.rabl new file mode 100644 index 000000000..3fb61489a --- /dev/null +++ b/app/views/api/v2/ansible_roles/show.json.rabl @@ -0,0 +1,3 @@ +object @ansible_role + +attributes :name, :created_at, :updated_at diff --git a/config/routes.rb b/config/routes.rb index 0e470b978..7264c7e1d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,5 +17,21 @@ post :confirm_import end end + + namespace :api do + scope '(:apiv)', + :module => :v2, + :defaults => { :apiv => 'v2' }, + :apiv => /v1|v2/, + :constraints => ApiConstraints.new(:version => 2) do + + resources :ansible_roles, :only => [:show, :index, :destroy] do + collection do + put :import + put :obsolete + end + end + end + end end end diff --git a/lib/foreman_ansible/engine.rb b/lib/foreman_ansible/engine.rb index 506b094cf..e71946fc7 100644 --- a/lib/foreman_ansible/engine.rb +++ b/lib/foreman_ansible/engine.rb @@ -32,10 +32,12 @@ class Engine < ::Rails::Engine { :hosts => [:play_roles, :multiple_play_roles] }, :resource_type => 'Host::Managed' permission :view_ansible_roles, - { :ansible_roles => [:index] }, + { :ansible_roles => [:index], + :'api/v2/ansible_roles' => [:index] }, :resource_type => 'AnsibleRole' permission :destroy_ansible_roles, - { :ansible_roles => [:destroy] }, + { :ansible_roles => [:destroy], + :'api/v2/ansible_roles' => [:destroy] }, :resource_type => 'AnsibleRole' permission :import_ansible_roles, { :ansible_roles => [:import, :confirm_import] }, diff --git a/test/functional/api/v2/ansible_roles_controller_test.rb b/test/functional/api/v2/ansible_roles_controller_test.rb new file mode 100644 index 000000000..3d27884b3 --- /dev/null +++ b/test/functional/api/v2/ansible_roles_controller_test.rb @@ -0,0 +1,24 @@ +require 'test_plugin_helper' + +module Api + module V2 + class AnsibleRolesControllerTest < ActionController::TestCase + setup do + @role = FactoryGirl.create(:ansible_role) + end + + test "should get index" do + get :index, {}, set_session_user + response = JSON.parse(@response.body) + assert response['results'].length > 0 + assert_response :success + end + + test "should destroy" do + delete :destroy, { :id => @role.id }, set_session_user + assert_response :ok + refute AnsibleRole.exists?(@role.id) + end + end + end +end diff --git a/test/unit/services/api_roles_importer_test.rb b/test/unit/services/api_roles_importer_test.rb new file mode 100644 index 000000000..87dfe5a09 --- /dev/null +++ b/test/unit/services/api_roles_importer_test.rb @@ -0,0 +1,28 @@ +require 'test_plugin_helper' +# unit tests for ApiRolesImporter +class ApiRolesImporterTest < ActiveSupport::TestCase + setup do + @importer = ForemanAnsible::ApiRolesImporter.new + first_name = 'test_user.test_name' + second_name = 'some_user.some_role' + @test_roles = [AnsibleRole.new(:name => first_name), + AnsibleRole.new(:name => second_name)] + end + + test 'should import roles' do + @importer.stubs(:import_role_names).returns(:new => @test_roles) + res = @importer.import! + assert_equal 2, res.count + assert AnsibleRole.find_by :name => @test_roles.first.name + assert AnsibleRole.find_by :name => @test_roles.last.name + end + + test 'should obsolete roles' do + @importer.stubs(:import_role_names).returns(:obsolete => @test_roles) + @test_roles.map(&:save) + res = @importer.obsolete! + assert_equal 2, res.count + refute AnsibleRole.find_by :name => @test_roles.first.name + refute AnsibleRole.find_by :name => @test_roles.last.name + end +end diff --git a/test/unit/services/roles_importer_test.rb b/test/unit/services/roles_importer_test.rb index 6b532a524..c033afcf3 100644 --- a/test/unit/services/roles_importer_test.rb +++ b/test/unit/services/roles_importer_test.rb @@ -14,28 +14,4 @@ class RolesImporterTest < ActiveSupport::TestCase assert_equal role.name, changes[:new].first.name assert_equal @role.name, changes[:obsolete].first.name end - - test 'should create new role' do - changed_roles - refute AnsibleRole.find_by(:name => @new_role[:name]) - @importer.create_new_roles(@changes['new']) - assert AnsibleRole.find_by(:name => @new_role[:name]) - end - - test 'should delete old roles' do - changed_roles - assert AnsibleRole.find_by(:name => @role.name) - @importer.delete_old_roles(@changes['obsolete']) - refute AnsibleRole.find_by(:name => @role.name) - end - - private - - def changed_roles - new_role_name = 'test_role.foreman' - @new_role = { :id => nil, :name => new_role_name } - @changes = { 'new' => { 'test_role.foreman' => @new_role.to_json }, - 'obsolete' => { @role.name => @role.to_json } - } - end end diff --git a/test/unit/services/ui_roles_importer_test.rb b/test/unit/services/ui_roles_importer_test.rb new file mode 100644 index 000000000..6715f1e79 --- /dev/null +++ b/test/unit/services/ui_roles_importer_test.rb @@ -0,0 +1,31 @@ +require 'test_plugin_helper' +# unit tests for UiRolesImporter +class UiRolesImporterTest < ActiveSupport::TestCase + setup do + changed_roles + @importer = ForemanAnsible::UiRolesImporter.new + end + + test 'should create new role' do + refute AnsibleRole.find_by(:name => @new_role[:name]) + @importer.create_new_roles(@changes['new']) + assert AnsibleRole.find_by(:name => @new_role[:name]) + end + + test 'should delete old roles' do + assert AnsibleRole.find_by(:name => @role.name) + @importer.delete_old_roles(@changes['obsolete']) + refute AnsibleRole.find_by(:name => @role.name) + end + + private + + def changed_roles + @role = FactoryGirl.create(:ansible_role) + new_role_name = 'test_role.foreman' + @new_role = { :id => nil, :name => new_role_name } + @changes = { 'new' => { 'test_role.foreman' => @new_role.to_json }, + 'obsolete' => { @role.name => @role.to_json } + } + end +end \ No newline at end of file