smart_proxy_ansible 3.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c78dd643ea513a8ffc694a01c86f12495aff1e1b14c7e106db9d29ab529d26d
4
- data.tar.gz: 8b2fde1a83bf65fb55fc122ce8ff931fd314f5289f9cf34e255d85959bebb653
3
+ metadata.gz: 9ae7dee62b09f9ea49553d1e2efab2b3f535940be697cf132af28fe0096da30f
4
+ data.tar.gz: 1997fbe1d05cedb27ee1a8c05d38b2644a25b84e0fbd11a93f201c8f81647643
5
5
  SHA512:
6
- metadata.gz: 695b0b451cc16439fd4fb76f9567f49e971c609c6d4696c19afa5feb3b90ac6a069e392ce38b177111041cae577662683cb6f594e86adc1096a6bfb5d7ac7ccc
7
- data.tar.gz: 94a5bac7bce83785b0cc9067e9ffd20e9084e8befd575c3f6fae7da5ac7e78b202bf137daa36e6e726a8fd54de190180e42ac1af96ff6b2fcb1b9381364f2a59
6
+ metadata.gz: 30b1c1acfd7ca623ab1bc8ccbbd83cfa0b31c9c99516ea8414e500470fa67b9755a579f3f87d77abaca565f9b31ff1503b335224f7664c40bda8d3e7e4707ddd
7
+ data.tar.gz: 9135ef0523d5246271da5b559384d7a53b952a9da67301c00b975d3544e365fd73af156b1cdd77989af75ea98990b1d78c33cd8f7ad040dfd0fa62cfb6103645
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'smart_proxy_dynflow/action/runner'
4
+
5
+ module Proxy::Ansible
6
+ module Actions
7
+ # Action that can be run both on Foreman or Foreman proxy side
8
+ # to execute the playbook run
9
+ class RunPlaybook < Proxy::Dynflow::Action::Runner
10
+ def initiate_runner
11
+ Proxy::Ansible::Runner::Playbook.new(
12
+ input[:inventory],
13
+ input[:playbook],
14
+ input[:options]
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
@@ -35,8 +35,8 @@ module Proxy
35
35
  role_name_parts = role_name.split('.')
36
36
  if role_name_parts.count == 3
37
37
  RolesReader.collections_paths.split(':').each do |path|
38
- variables[role_name] = VariablesExtractor
39
- .extract_variables("#{path}/ansible_collections/#{role_name_parts[0]}/#{role_name_parts[1]}/roles/#{role_name_parts[2]}") if variables[role_name].nil? || variables[role_name].empty?
38
+ variables[role_name] ||= VariablesExtractor
39
+ .extract_variables("#{path}/ansible_collections/#{role_name_parts[0]}/#{role_name_parts[1]}/roles/#{role_name_parts[2]}")
40
40
  end
41
41
  else
42
42
  RolesReader.roles_path.split(':').each do |path|
@@ -5,16 +5,24 @@ module Proxy
5
5
  rackup_path File.expand_path('http_config.ru', __dir__)
6
6
  settings_file 'ansible.yml'
7
7
  plugin :ansible, Proxy::Ansible::VERSION
8
+ default_settings :ansible_dir => Dir.home
9
+ # :working_dir => nil
8
10
 
9
11
  after_activation do
10
- begin
11
- require 'smart_proxy_dynflow_core'
12
- require 'foreman_ansible_core'
13
- ForemanAnsibleCore.initialize_settings(Proxy::Ansible::Plugin.settings.to_h)
14
- rescue LoadError => _
15
- # Dynflow core is not available in the proxy, will be handled
16
- # by standalone Dynflow core
17
- end
12
+ require 'smart_proxy_dynflow'
13
+ require 'smart_proxy_dynflow/continuous_output'
14
+ require 'smart_proxy_ansible/task_launcher/ansible_runner'
15
+ require 'smart_proxy_ansible/task_launcher/playbook'
16
+ require 'smart_proxy_ansible/actions'
17
+ require 'smart_proxy_ansible/remote_execution_core/ansible_runner'
18
+ require 'smart_proxy_ansible/runner/ansible_runner'
19
+ require 'smart_proxy_ansible/runner/command_creator'
20
+ require 'smart_proxy_ansible/runner/playbook'
21
+
22
+ Proxy::Dynflow::TaskLauncherRegistry.register('ansible-runner',
23
+ TaskLauncher::AnsibleRunner)
24
+ Proxy::Dynflow::TaskLauncherRegistry.register('ansible-playbook',
25
+ TaskLauncher::Playbook)
18
26
  end
19
27
  end
20
28
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'smart_proxy_dynflow/runner/command_runner'
4
+
5
+ module Proxy::Ansible
6
+ module RemoteExecutionCore
7
+ # Takes an inventory and runs it through REXCore CommandRunner
8
+ class AnsibleRunner < ::Proxy::Dynflow::Runner::CommandRunner
9
+ DEFAULT_REFRESH_INTERVAL = 1
10
+ CONNECTION_PROMPT = 'Are you sure you want to continue connecting (yes/no)? '
11
+
12
+ def initialize(options, suspended_action:)
13
+ super(options, :suspended_action => suspended_action)
14
+ @playbook_runner = Proxy::Ansible::Runner::Playbook.new(
15
+ options['ansible_inventory'],
16
+ options['script'],
17
+ options,
18
+ :suspended_action => suspended_action
19
+ )
20
+ end
21
+
22
+ def start
23
+ @playbook_runner.logger = logger
24
+ @playbook_runner.start
25
+ rescue StandardError => e
26
+ logger.error(
27
+ 'error while initalizing command'\
28
+ " #{e.class} #{e.message}:\n #{e.backtrace.join("\n")}"
29
+ )
30
+ publish_exception('Error initializing command', e)
31
+ end
32
+
33
+ def fill_continuous_output(continuous_output)
34
+ delegated_output.fetch('result', []).each do |raw_output|
35
+ continuous_output.add_raw_output(raw_output)
36
+ end
37
+ rescue StandardError => e
38
+ continuous_output.add_exception(_('Error loading data from proxy'), e)
39
+ end
40
+
41
+ def refresh
42
+ @command_out = @playbook_runner.command_out
43
+ @command_in = @playbook_runner.command_in
44
+ @command_pid = @playbook_runner.command_pid
45
+ super
46
+ kill if unknown_host_key_fingerprint?
47
+ end
48
+
49
+ def kill
50
+ publish_exit_status(1)
51
+ ::Process.kill('SIGTERM', @command_pid)
52
+ close
53
+ end
54
+
55
+ private
56
+
57
+ def unknown_host_key_fingerprint?
58
+ last_output = @continuous_output.raw_outputs.last
59
+ return if last_output.nil?
60
+ last_output['output']&.lines&.last == CONNECTION_PROMPT
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,210 @@
1
+ require 'shellwords'
2
+
3
+ require 'smart_proxy_dynflow/runner/command'
4
+ require 'smart_proxy_dynflow/runner/base'
5
+ require 'smart_proxy_dynflow/runner/parent'
6
+ module Proxy::Ansible
7
+ module Runner
8
+ class AnsibleRunner < ::Proxy::Dynflow::Runner::Parent
9
+ include ::Proxy::Dynflow::Runner::Command
10
+
11
+ def initialize(input, suspended_action:)
12
+ super input, :suspended_action => suspended_action
13
+ @inventory = rebuild_secrets(rebuild_inventory(input), input)
14
+ action_input = input.values.first[:input][:action_input]
15
+ @playbook = action_input[:script]
16
+ @root = working_dir
17
+ @verbosity_level = action_input[:verbosity_level]
18
+ @rex_command = action_input[:remote_execution_command]
19
+ @check_mode = action_input[:check_mode]
20
+ @tags = action_input[:tags]
21
+ @tags_flag = action_input[:tags_flag]
22
+ end
23
+
24
+ def start
25
+ prepare_directory_structure
26
+ write_inventory
27
+ write_playbook
28
+ start_ansible_runner
29
+ end
30
+
31
+ def refresh
32
+ return unless super
33
+ @counter ||= 1
34
+ @uuid ||= File.basename(Dir["#{@root}/artifacts/*"].first)
35
+ job_event_dir = File.join(@root, 'artifacts', @uuid, 'job_events')
36
+ loop do
37
+ files = Dir["#{job_event_dir}/*.json"].map do |file|
38
+ num = File.basename(file)[/\A\d+/].to_i unless file.include?('partial')
39
+ [file, num]
40
+ end
41
+ files_with_nums = files.select { |(_, num)| num && num >= @counter }.sort_by(&:last)
42
+ break if files_with_nums.empty?
43
+ logger.debug("[foreman_ansible] - processing event files: #{files_with_nums.map(&:first).inspect}}")
44
+ files_with_nums.map(&:first).each { |event_file| handle_event_file(event_file) }
45
+ @counter = files_with_nums.last.last + 1
46
+ end
47
+ end
48
+
49
+ def close
50
+ super
51
+ FileUtils.remove_entry(@root) if @tmp_working_dir
52
+ end
53
+
54
+ private
55
+
56
+ def handle_event_file(event_file)
57
+ logger.debug("[foreman_ansible] - parsing event file #{event_file}")
58
+ begin
59
+ event = JSON.parse(File.read(event_file))
60
+ if (hostname = event.dig('event_data', 'host'))
61
+ handle_host_event(hostname, event)
62
+ else
63
+ handle_broadcast_data(event)
64
+ end
65
+ true
66
+ rescue JSON::ParserError => e
67
+ logger.error("[foreman_ansible] - Error parsing runner event at #{event_file}: #{e.class}: #{e.message}")
68
+ logger.debug(e.backtrace.join("\n"))
69
+ end
70
+ end
71
+
72
+ def handle_host_event(hostname, event)
73
+ log_event("for host: #{hostname.inspect}", event)
74
+ publish_data_for(hostname, event['stdout'] + "\n", 'stdout') if event['stdout']
75
+ case event['event']
76
+ when 'runner_on_ok'
77
+ publish_exit_status_for(hostname, 0) if @exit_statuses[hostname].nil?
78
+ when 'runner_on_unreachable'
79
+ publish_exit_status_for(hostname, 1)
80
+ when 'runner_on_failed'
81
+ publish_exit_status_for(hostname, 2) if event.dig('event_data', 'ignore_errors').nil?
82
+ end
83
+ end
84
+
85
+ def handle_broadcast_data(event)
86
+ log_event("broadcast", event)
87
+ if event['event'] == 'playbook_on_stats'
88
+ header, *rows = event['stdout'].strip.lines.map(&:chomp)
89
+ @outputs.keys.select { |key| key.is_a? String }.each do |host|
90
+ line = rows.find { |row| row =~ /#{host}/ }
91
+ publish_data_for(host, [header, line].join("\n"), 'stdout')
92
+ end
93
+ else
94
+ broadcast_data(event['stdout'] + "\n", 'stdout')
95
+ end
96
+ end
97
+
98
+ def write_inventory
99
+ path = File.join(@root, 'inventory', 'hosts')
100
+ data_path = File.join(@root, 'data')
101
+ inventory_script = <<~INVENTORY_SCRIPT
102
+ #!/bin/sh
103
+ cat #{::Shellwords.escape data_path}
104
+ INVENTORY_SCRIPT
105
+ File.write(path, inventory_script)
106
+ File.write(data_path, JSON.dump(@inventory))
107
+ File.chmod(0o0755, path)
108
+ end
109
+
110
+ def write_playbook
111
+ File.write(File.join(@root, 'project', 'playbook.yml'), @playbook)
112
+ end
113
+
114
+ def start_ansible_runner
115
+ env = {}
116
+ env['FOREMAN_CALLBACK_DISABLE'] = '1' if @rex_command
117
+ command = [env, 'ansible-runner', 'run', @root, '-p', 'playbook.yml']
118
+ command << '--cmdline' << cmdline unless cmdline.nil?
119
+ command << verbosity if verbose?
120
+ initialize_command(*command)
121
+ logger.debug("[foreman_ansible] - Running command '#{command.join(' ')}'")
122
+ end
123
+
124
+ def cmdline
125
+ cmd_args = [tags_cmd, check_cmd].reject(&:empty?)
126
+ return nil unless cmd_args.any?
127
+ cmd_args.join(' ')
128
+ end
129
+
130
+ def tags_cmd
131
+ flag = @tags_flag == 'include' ? '--tags' : '--skip-tags'
132
+ @tags.empty? ? '' : "#{flag} '#{Array(@tags).join(',')}'"
133
+ end
134
+
135
+ def check_cmd
136
+ check_mode? ? '--check' : ''
137
+ end
138
+
139
+ def verbosity
140
+ '-' + 'v' * @verbosity_level.to_i
141
+ end
142
+
143
+ def verbose?
144
+ @verbosity_level.to_i.positive?
145
+ end
146
+
147
+ def check_mode?
148
+ @check_mode == true
149
+ end
150
+
151
+ def prepare_directory_structure
152
+ inner = %w[inventory project].map { |part| File.join(@root, part) }
153
+ ([@root] + inner).each do |path|
154
+ FileUtils.mkdir_p path
155
+ end
156
+ end
157
+
158
+ def log_event(description, event)
159
+ # TODO: replace this ugly code with block variant once https://github.com/Dynflow/dynflow/pull/323
160
+ # arrives in production
161
+ logger.debug("[foreman_ansible] - handling event #{description}: #{JSON.pretty_generate(event)}") if logger.level <= ::Logger::DEBUG
162
+ end
163
+
164
+ # Each per-host task has inventory only for itself, we must
165
+ # collect all the partial inventories into one large inventory
166
+ # containing all the hosts.
167
+ def rebuild_inventory(input)
168
+ action_inputs = input.values.map { |hash| hash[:input][:action_input] }
169
+ hostnames = action_inputs.map { |hash| hash[:name] }
170
+ inventories = action_inputs.map { |hash| hash[:ansible_inventory] }
171
+ host_vars = inventories.map { |i| i['_meta']['hostvars'] }.reduce({}) do |acc, hosts|
172
+ hosts.reduce(acc) do |inner_acc, (hostname, vars)|
173
+ vars[:ansible_ssh_private_key_file] ||= Proxy::RemoteExecution::Ssh::Plugin.settings[:ssh_identity_key_file]
174
+ inner_acc.merge(hostname => vars)
175
+ end
176
+ end
177
+
178
+ { '_meta' => { 'hostvars' => host_vars },
179
+ 'all' => { 'hosts' => hostnames,
180
+ 'vars' => inventories.first['all']['vars'] } }
181
+ end
182
+
183
+ def working_dir
184
+ return @root if @root
185
+ dir = Proxy::Ansible::Plugin.settings[:working_dir]
186
+ @tmp_working_dir = true
187
+ if dir.nil?
188
+ Dir.mktmpdir
189
+ else
190
+ Dir.mktmpdir(nil, File.expand_path(dir))
191
+ end
192
+ end
193
+
194
+ def rebuild_secrets(inventory, input)
195
+ input.each do |host, host_input|
196
+ secrets = host_input['input']['action_input']['secrets']
197
+ per_host = secrets['per-host'][host]
198
+
199
+ new_secrets = {
200
+ 'ansible_password' => inventory['ssh_password'] || per_host['ansible_password'],
201
+ 'ansible_become_password' => inventory['effective_user_password'] || per_host['ansible_become_password']
202
+ }
203
+ inventory['_meta']['hostvars'][host].update(new_secrets)
204
+ end
205
+
206
+ inventory
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proxy::Ansible
4
+ # Creates the actual command to be passed to smart_proxy_dynflow to run
5
+ class CommandCreator
6
+ def initialize(inventory_file, playbook_file, options = {})
7
+ @options = options
8
+ @playbook_file = playbook_file
9
+ @inventory_file = inventory_file
10
+ command
11
+ end
12
+
13
+ def command
14
+ parts = [environment_variables]
15
+ parts << 'ansible-playbook'
16
+ parts.concat(command_options)
17
+ parts << @playbook_file
18
+ parts
19
+ end
20
+
21
+ private
22
+
23
+ def environment_variables
24
+ defaults = { 'JSON_INVENTORY_FILE' => @inventory_file }
25
+ defaults['ANSIBLE_CALLBACK_WHITELIST'] = '' if rex_command?
26
+ defaults
27
+ end
28
+
29
+ def command_options
30
+ opts = ['-i', json_inventory_script]
31
+ opts.concat([setup_verbosity]) if verbose?
32
+ opts.concat(['-T', @options[:timeout]]) unless @options[:timeout].nil?
33
+ opts
34
+ end
35
+
36
+ def json_inventory_script
37
+ File.expand_path('../../../bin/json_inventory.sh', File.dirname(__FILE__))
38
+ end
39
+
40
+ def setup_verbosity
41
+ verbosity_level = @options[:verbosity_level].to_i
42
+ verbosity = '-'
43
+ verbosity_level.times do
44
+ verbosity += 'v'
45
+ end
46
+ verbosity
47
+ end
48
+
49
+ def verbose?
50
+ verbosity_level = @options[:verbosity_level]
51
+ # rubocop:disable Rails/Present
52
+ !verbosity_level.nil? && !verbosity_level.empty? &&
53
+ verbosity_level.to_i.positive?
54
+ # rubocop:enable Rails/Present
55
+ end
56
+
57
+ def rex_command?
58
+ @options[:remote_execution_command]
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'smart_proxy_dynflow/continuous_output'
4
+ require 'smart_proxy_dynflow/runner/command_runner'
5
+ require_relative 'command_creator'
6
+ require 'tmpdir'
7
+ require 'net/ssh'
8
+
9
+ module Proxy::Ansible
10
+ module Runner
11
+ # Implements Proxy::Dynflow::Runner::Base interface for running
12
+ # Ansible playbooks, used by the Foreman Ansible plugin and Ansible proxy
13
+ class Playbook < Proxy::Dynflow::Runner::CommandRunner
14
+ attr_reader :command_out, :command_in, :command_pid
15
+
16
+ def initialize(inventory, playbook, options = {}, suspended_action:)
17
+ super :suspended_action => suspended_action
18
+ @inventory = rebuild_secrets(inventory, options[:secrets])
19
+ unknown_hosts.each do |host|
20
+ add_to_known_hosts(host)
21
+ end
22
+ @playbook = playbook
23
+ @options = options
24
+ initialize_dirs
25
+ end
26
+
27
+ def start
28
+ write_inventory
29
+ write_playbook
30
+ command = CommandCreator.new(inventory_file,
31
+ playbook_file,
32
+ @options).command
33
+ logger.debug('[foreman_ansible] - Initializing Ansible Runner')
34
+ Dir.chdir(@ansible_dir) do
35
+ initialize_command(*command)
36
+ logger.debug("[foreman_ansible] - Running command '#{command.join(' ')}'")
37
+ end
38
+ end
39
+
40
+ def kill
41
+ publish_data('== TASK ABORTED BY USER ==', 'stdout')
42
+ publish_exit_status(1)
43
+ ::Process.kill('SIGTERM', @command_pid)
44
+ close
45
+ end
46
+
47
+ def close
48
+ super
49
+ FileUtils.remove_entry(@working_dir) if @tmp_working_dir
50
+ end
51
+
52
+ private
53
+
54
+ def write_inventory
55
+ ensure_directory(File.dirname(inventory_file))
56
+ File.write(inventory_file, JSON.dump(@inventory))
57
+ end
58
+
59
+ def write_playbook
60
+ ensure_directory(File.dirname(playbook_file))
61
+ File.write(playbook_file, @playbook)
62
+ end
63
+
64
+ def inventory_file
65
+ File.join(@working_dir, 'foreman-inventories', id)
66
+ end
67
+
68
+ def playbook_file
69
+ File.join(@working_dir, "foreman-playbook-#{id}.yml")
70
+ end
71
+
72
+ def events_dir
73
+ File.join(@working_dir, 'events', id.to_s)
74
+ end
75
+
76
+ def ensure_directory(path)
77
+ if File.exist?(path)
78
+ raise "#{path} expected to be a directory" unless File.directory?(path)
79
+ else
80
+ FileUtils.mkdir_p(path)
81
+ end
82
+ path
83
+ end
84
+
85
+ def initialize_dirs
86
+ settings = Proxy::Ansible::Plugin.settings
87
+ initialize_working_dir(settings[:working_dir])
88
+ initialize_ansible_dir(settings[:ansible_dir])
89
+ end
90
+
91
+ def initialize_working_dir(working_dir)
92
+ if working_dir.nil?
93
+ @working_dir = Dir.mktmpdir
94
+ @tmp_working_dir = true
95
+ else
96
+ @working_dir = File.expand_path(working_dir)
97
+ end
98
+ end
99
+
100
+ def initialize_ansible_dir(ansible_dir)
101
+ raise "Ansible dir #{ansible_dir} does not exist" unless
102
+ !ansible_dir.nil? && File.exist?(ansible_dir)
103
+ @ansible_dir = ansible_dir
104
+ end
105
+
106
+ def unknown_hosts
107
+ @inventory['all']['hosts'].select do |host|
108
+ Net::SSH::KnownHosts.search_for(host).empty?
109
+ end
110
+ end
111
+
112
+ def add_to_known_hosts(host)
113
+ logger.warn("[foreman_ansible] - Host #{host} not found in known_hosts")
114
+ Net::SSH::Transport::Session.new(host).host_keys.each do |host_key|
115
+ Net::SSH::KnownHosts.add(host, host_key)
116
+ end
117
+ logger.warn("[foreman_ansible] - Added host key #{host} to known_hosts")
118
+ rescue StandardError => e
119
+ logger.error('[foreman_ansible] - Failed to save host key for '\
120
+ "#{host}: #{e}")
121
+ end
122
+
123
+ def rebuild_secrets(inventory, secrets)
124
+ inventory['all']['hosts'].each do |name|
125
+ per_host = secrets['per-host'][name]
126
+
127
+ new_secrets = {
128
+ 'ansible_password' => inventory['ssh_password'] || per_host['ansible_password'],
129
+ 'ansible_become_password' => inventory['effective_user_password'] || per_host['ansible_become_password']
130
+ }
131
+ inventory['_meta']['hostvars'][name].update(new_secrets)
132
+ end
133
+
134
+ inventory
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,48 @@
1
+ require 'fileutils'
2
+ require 'smart_proxy_dynflow/task_launcher/abstract'
3
+ require 'smart_proxy_dynflow/task_launcher/batch'
4
+ require 'smart_proxy_dynflow/task_launcher/group'
5
+ require 'smart_proxy_ansible/runner/ansible_runner'
6
+
7
+ module Proxy::Ansible
8
+ module TaskLauncher
9
+ class AnsibleRunner < Proxy::Dynflow::TaskLauncher::AbstractGroup
10
+ def runner_input(input)
11
+ super(input).reduce({}) do |acc, (_id, data)|
12
+ acc.merge(data[:input]['action_input']['name'] => data)
13
+ end
14
+ end
15
+
16
+ def operation
17
+ 'ansible-runner'
18
+ end
19
+
20
+ def self.runner_class
21
+ Runner::AnsibleRunner
22
+ end
23
+
24
+ # Discard everything apart from hostname to be able to tell the actions
25
+ # apart when debugging
26
+ def transform_input(input)
27
+ { 'action_input' => input['action_input'].slice('name') }
28
+ end
29
+
30
+ # def self.input_format
31
+ # {
32
+ # $UUID => {
33
+ # :execution_plan_id => $EXECUTION_PLAN_UUID,
34
+ # :run_step_id => Integer,
35
+ # :input => {
36
+ # :action_class => Class,
37
+ # :action_input => {
38
+ # "ansible_inventory"=> String,
39
+ # "hostname"=>"127.0.0.1",
40
+ # "script"=>"---\n- hosts: all\n tasks:\n - shell: |\n true\n register: out\n - debug: var=out"
41
+ # }
42
+ # }
43
+ # }
44
+ # }
45
+ # end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ require 'smart_proxy_dynflow/action/runner'
2
+
3
+ module Proxy::Ansible
4
+ module TaskLauncher
5
+ class Playbook < Proxy::Dynflow::TaskLauncher::Batch
6
+ class PlaybookRunnerAction < Proxy::Dynflow::Action::Runner
7
+ def initiate_runner
8
+ additional_options = {
9
+ :step_id => run_step_id,
10
+ :uuid => execution_plan_id
11
+ }
12
+ ::Proxy::Ansible::RemoteExecutionCore::AnsibleRunner.new(
13
+ input.merge(additional_options),
14
+ :suspended_action => suspended_action
15
+ )
16
+ end
17
+ end
18
+
19
+ def child_launcher(parent)
20
+ ::Proxy::Dynflow::TaskLauncher::Single.new(world, callback, :parent => parent,
21
+ :action_class_override => PlaybookRunnerAction)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,6 +2,6 @@ module Proxy
2
2
  # Version, this allows the proxy and other plugins know
3
3
  # what version of the Ansible plugin is running
4
4
  module Ansible
5
- VERSION = '3.1.1'
5
+ VERSION = '3.2.0'
6
6
  end
7
7
  end
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  :enabled: true
3
- :ansible_working_dir: '~/.foreman-ansible'
3
+ :working_dir: '~/.foreman-ansible'
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_ansible
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
8
8
  - Daniel Lobato
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-08-09 00:00:00.000000000 Z
12
+ date: 2021-06-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -95,20 +95,48 @@ dependencies:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: net-ssh
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
98
112
  - !ruby/object:Gem::Dependency
99
113
  name: smart_proxy_dynflow
100
114
  requirement: !ruby/object:Gem::Requirement
101
115
  requirements:
102
116
  - - "~>"
103
117
  - !ruby/object:Gem::Version
104
- version: '0.1'
118
+ version: '0.5'
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '0.5'
126
+ - !ruby/object:Gem::Dependency
127
+ name: smart_proxy_remote_execution_ssh
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '0.4'
105
133
  type: :runtime
106
134
  prerelease: false
107
135
  version_requirements: !ruby/object:Gem::Requirement
108
136
  requirements:
109
137
  - - "~>"
110
138
  - !ruby/object:Gem::Version
111
- version: '0.1'
139
+ version: '0.4'
112
140
  description: " Smart-Proxy ansible plugin\n"
113
141
  email:
114
142
  - inecas@redhat.com
@@ -123,11 +151,18 @@ files:
123
151
  - README.md
124
152
  - bundler.d/ansible.rb
125
153
  - lib/smart_proxy_ansible.rb
154
+ - lib/smart_proxy_ansible/actions.rb
126
155
  - lib/smart_proxy_ansible/api.rb
127
156
  - lib/smart_proxy_ansible/exception.rb
128
157
  - lib/smart_proxy_ansible/http_config.ru
129
158
  - lib/smart_proxy_ansible/plugin.rb
159
+ - lib/smart_proxy_ansible/remote_execution_core/ansible_runner.rb
130
160
  - lib/smart_proxy_ansible/roles_reader.rb
161
+ - lib/smart_proxy_ansible/runner/ansible_runner.rb
162
+ - lib/smart_proxy_ansible/runner/command_creator.rb
163
+ - lib/smart_proxy_ansible/runner/playbook.rb
164
+ - lib/smart_proxy_ansible/task_launcher/ansible_runner.rb
165
+ - lib/smart_proxy_ansible/task_launcher/playbook.rb
131
166
  - lib/smart_proxy_ansible/variables_extractor.rb
132
167
  - lib/smart_proxy_ansible/version.rb
133
168
  - settings.d/ansible.yml.example
@@ -135,7 +170,7 @@ homepage: https://github.com/theforeman/smart_proxy_ansible
135
170
  licenses:
136
171
  - GPL-3.0
137
172
  metadata: {}
138
- post_install_message:
173
+ post_install_message:
139
174
  rdoc_options: []
140
175
  require_paths:
141
176
  - lib
@@ -151,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
186
  version: '0'
152
187
  requirements: []
153
188
  rubygems_version: 3.1.2
154
- signing_key:
189
+ signing_key:
155
190
  specification_version: 4
156
191
  summary: Smart-Proxy Ansible plugin
157
192
  test_files: []