smart_proxy_ansible 3.1.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []