smart_proxy_acd 0.2.0 → 0.3.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 +4 -4
- data/lib/smart_proxy_acd/acd.rb +11 -2
- data/lib/smart_proxy_acd/acd_runner.rb +190 -0
- data/lib/smart_proxy_acd/acd_task_launcher.rb +25 -0
- data/lib/smart_proxy_acd/version.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 881bee215ecdb92ddd19b7b60397274f9e5eb72911660a289d235b5ab09aa8bc
|
4
|
+
data.tar.gz: 25abeb5af5ca858c7a18fbce66b4727a8bfe7d8b120089a7ecfd370792a75931
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05a4fa40b1df875b7526b584b2ac5a2a544abd3d2248aa04dc31bc8b4592f094163929e6790d3abd826f5d7c9d0401a2add3476b5dc813aae027e5cd0354ffa1
|
7
|
+
data.tar.gz: ae1ad0260d9c7c95c3ae78be4bcc05e68a679ff6c3d453311598dcf2c51c4428c8e03056b2293ae4fcc85480566da7e48a5576e8473783f5816668b388d05f0c
|
data/lib/smart_proxy_acd/acd.rb
CHANGED
@@ -5,8 +5,17 @@ module Proxy
|
|
5
5
|
# Implement a SmartProxy Plugin
|
6
6
|
class Plugin < ::Proxy::Plugin
|
7
7
|
plugin 'acd', Proxy::Acd::VERSION
|
8
|
-
|
9
|
-
|
8
|
+
rackup_path File.expand_path('acd_http_config.ru', __dir__)
|
9
|
+
|
10
|
+
load_classes do
|
11
|
+
require 'smart_proxy_dynflow'
|
12
|
+
require 'smart_proxy_acd/acd_runner'
|
13
|
+
require 'smart_proxy_acd/acd_task_launcher'
|
14
|
+
end
|
15
|
+
|
16
|
+
load_dependency_injection_wirings do |_container_instance, _settings|
|
17
|
+
Proxy::Dynflow::TaskLauncherRegistry.register('acd', AcdTaskLauncher)
|
18
|
+
end
|
10
19
|
end
|
11
20
|
end
|
12
21
|
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'smart_proxy_dynflow/runner/command_runner'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'rest-client'
|
4
|
+
require 'tmpdir'
|
5
|
+
require 'socket'
|
6
|
+
|
7
|
+
# rubocop:disable ClassLength
|
8
|
+
|
9
|
+
module Proxy
|
10
|
+
module Acd
|
11
|
+
# Implements the AcdRunner to be used by foreman_remote_execution
|
12
|
+
class AcdRunner < Proxy::Dynflow::Runner::CommandRunner
|
13
|
+
DEFAULT_REFRESH_INTERVAL = 1
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def parse_dynflow_settings(path)
|
17
|
+
return @dynflow_settings if defined? @dynflow_settings
|
18
|
+
if File.exist?(path)
|
19
|
+
@dynflow_settings = {}
|
20
|
+
YAML.load_file(path).each do |key, value|
|
21
|
+
@dynflow_settings[key.to_s] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
@dynflow_settings
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_ssl_options
|
28
|
+
return @ssl_options if defined? @ssl_options
|
29
|
+
|
30
|
+
@ssl_options = {}
|
31
|
+
return @ssl_options unless URI.parse(@dynflow_settings['foreman_url']).scheme == 'https'
|
32
|
+
|
33
|
+
@ssl_options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
|
34
|
+
|
35
|
+
private_key_file = @dynflow_settings['foreman_ssl_key'] || @dynflow_settings['ssl_private_key']
|
36
|
+
if private_key_file
|
37
|
+
private_key = File.read(private_key_file)
|
38
|
+
@ssl_options[:ssl_client_key] = OpenSSL::PKey::RSA.new(private_key)
|
39
|
+
end
|
40
|
+
certificate_file = @dynflow_settings['foreman_ssl_cert'] || @dynflow_settings['ssl_certificate']
|
41
|
+
if certificate_file
|
42
|
+
certificate = File.read(certificate_file)
|
43
|
+
@ssl_options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(certificate)
|
44
|
+
end
|
45
|
+
ca_file = @dynflow_settings['foreman_ssl_ca'] || @dynflow_settings['ssl_ca_file']
|
46
|
+
@ssl_options[:ssl_ca_file] = ca_file if ca_file
|
47
|
+
@ssl_options
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(options, suspended_action:)
|
52
|
+
super(options, :suspended_action => suspended_action)
|
53
|
+
@options = options
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_playbook(playbook_id)
|
57
|
+
logger.debug("Get playbook with id #{playbook_id}")
|
58
|
+
response = playbook_resource(playbook_id).get
|
59
|
+
if response.code.to_s != '200'
|
60
|
+
raise "Failed performing callback to Foreman server: #{response.code} #{response.body}"
|
61
|
+
end
|
62
|
+
tmp_file = Tempfile.new.path
|
63
|
+
File.write(tmp_file, response)
|
64
|
+
@playbook_tmp_base64_file = tmp_file
|
65
|
+
end
|
66
|
+
|
67
|
+
def playbook_resource(playbook_id)
|
68
|
+
dynflow_settings = self.class.parse_dynflow_settings('/etc/foreman-proxy/settings.yml')
|
69
|
+
playbook_url = dynflow_settings['foreman_url'] + "/acd/api/v2/ansible_playbooks/#{playbook_id}/grab"
|
70
|
+
@resource ||= RestClient::Resource.new(playbook_url, self.class.parse_ssl_options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def store_playbook
|
74
|
+
logger.debug('Unpack ansible playbook')
|
75
|
+
dir = Dir.mktmpdir
|
76
|
+
raise 'Could not create temporary directory to run ansible playbook' if dir.nil? || !Dir.exist?(dir)
|
77
|
+
command = "base64 -d #{@playbook_tmp_base64_file} | tar xz -C #{dir}"
|
78
|
+
system(command)
|
79
|
+
@playbook_tmp_dir = dir
|
80
|
+
end
|
81
|
+
|
82
|
+
def cleanup
|
83
|
+
File.unlink(@playbook_tmp_base64_file) if File.exist?(@playbook_tmp_base64_file)
|
84
|
+
FileUtils.rm_rf(@playbook_tmp_dir) if Dir.exist?(@playbook_tmp_dir)
|
85
|
+
end
|
86
|
+
|
87
|
+
def start
|
88
|
+
parse_acd_job
|
89
|
+
|
90
|
+
publish_data("Grab playbook to configure application #{@application_name}...", 'stdout')
|
91
|
+
get_playbook(@playbook_id)
|
92
|
+
store_playbook
|
93
|
+
|
94
|
+
@playbook_path = File.join(@playbook_tmp_dir, @playbook_file)
|
95
|
+
raise "Could not run playbook: playbook file #{@playbook_file} not found in playbook dir #{@playbook_tmp_dir}" unless File.exist?(@playbook_path)
|
96
|
+
|
97
|
+
publish_data('Write temporary inventory', 'stdout')
|
98
|
+
write_inventory
|
99
|
+
|
100
|
+
command = generate_command
|
101
|
+
logger.debug("Running command #{command.join(' ')}")
|
102
|
+
initialize_command(*command)
|
103
|
+
end
|
104
|
+
|
105
|
+
def close
|
106
|
+
logger.debug("Cleanup ansible playbook #{@playbook_tmp_dir} and #{@playbook_tmp_base64_file}")
|
107
|
+
cleanup
|
108
|
+
end
|
109
|
+
|
110
|
+
def kill
|
111
|
+
publish_data('== TASK ABORTED BY USER ==', 'stdout')
|
112
|
+
publish_exit_status(1)
|
113
|
+
::Process.kill('SIGTERM', @command_pid)
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def parse_acd_job
|
119
|
+
@acd_job = YAML.safe_load(@options['script'])
|
120
|
+
@application_name = @acd_job['application_name']
|
121
|
+
@playbook_id = @acd_job['playbook_id']
|
122
|
+
@playbook_file = @acd_job['playbook_file']
|
123
|
+
|
124
|
+
raise "'playbook_file' need to be specified" if @playbook_file.nil? || @playbook_file.empty?
|
125
|
+
raise "'playbook_id' need to be specified" if @playbook_id.nil?
|
126
|
+
end
|
127
|
+
|
128
|
+
def proxy_hostname
|
129
|
+
Socket.gethostbyname(Socket.gethostname).first
|
130
|
+
end
|
131
|
+
|
132
|
+
def write_inventory
|
133
|
+
complete_inventory = YAML.safe_load(@acd_job['inventory'])
|
134
|
+
my_inventory = complete_inventory[proxy_hostname]
|
135
|
+
|
136
|
+
tmp_inventory_file = Tempfile.new('acd_inventory')
|
137
|
+
tmp_inventory_file << my_inventory.to_yaml
|
138
|
+
tmp_inventory_file << "\n"
|
139
|
+
tmp_inventory_file.close
|
140
|
+
@inventory_path = tmp_inventory_file.path
|
141
|
+
end
|
142
|
+
|
143
|
+
def environment
|
144
|
+
env = {}
|
145
|
+
env['ANSIBLE_CALLBACK_WHITELIST'] = ''
|
146
|
+
env['ANSIBLE_LOAD_CALLBACK_PLUGINS'] = '0'
|
147
|
+
env
|
148
|
+
end
|
149
|
+
|
150
|
+
def setup_verbosity
|
151
|
+
verbosity = ''
|
152
|
+
if @acd_job['verbose']
|
153
|
+
verbosity_level = @acd_job['verbose'].split(' ').first.to_i
|
154
|
+
if verbosity_level.positive?
|
155
|
+
verbosity = '-'
|
156
|
+
verbosity_level.times do
|
157
|
+
verbosity += 'v'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
verbosity
|
162
|
+
end
|
163
|
+
|
164
|
+
def valid_tags(tag)
|
165
|
+
re = /^\w+(\s*,\s*\w+)*$/
|
166
|
+
return true if @acd_job[tag] && @acd_job[tag].match?(re)
|
167
|
+
end
|
168
|
+
|
169
|
+
def generate_command
|
170
|
+
logger.debug("Generate command with #{@inventory_path} to run #{@playbook_id} with path #{@playbook_path}")
|
171
|
+
command = [environment]
|
172
|
+
command << 'ansible-playbook'
|
173
|
+
command << '-i'
|
174
|
+
command << @inventory_path
|
175
|
+
verbose = setup_verbosity
|
176
|
+
command << verbose unless verbose.empty?
|
177
|
+
command << "--tags '#{@acd_job['tags']}'" if valid_tags('tags')
|
178
|
+
command << "--skip-tags '#{@acd_job['skip_tags']}'" if valid_tags('skip_tags')
|
179
|
+
if @acd_job.key?('extra_vars') && !@acd_job['extra_vars'].nil? && !@acd_job['extra_vars'].empty?
|
180
|
+
command << '--extra-vars'
|
181
|
+
command << "'#{@acd_job['extra_vars']}'"
|
182
|
+
end
|
183
|
+
command << @playbook_path.to_s
|
184
|
+
command
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# rubocop:enable ClassLength
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Proxy
|
2
|
+
module Acd
|
3
|
+
# Implements the TaskLauncher::Batch for Acd
|
4
|
+
class AcdTaskLauncher < Proxy::Dynflow::TaskLauncher::Batch
|
5
|
+
# Implements the Runner::Action for Acd
|
6
|
+
class AcdRunnerAction < 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::Acd::AcdRunner.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 => AcdRunnerAction)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_acd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Bucher
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.50.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: smart_proxy_dynflow
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.0
|
27
41
|
description: Application Centric Deployment smart proxy plugin
|
28
42
|
email: info@atix.de
|
29
43
|
executables: []
|
@@ -40,6 +54,8 @@ files:
|
|
40
54
|
- lib/smart_proxy_acd/acd_api.rb
|
41
55
|
- lib/smart_proxy_acd/acd_http_config.ru
|
42
56
|
- lib/smart_proxy_acd/acd_main.rb
|
57
|
+
- lib/smart_proxy_acd/acd_runner.rb
|
58
|
+
- lib/smart_proxy_acd/acd_task_launcher.rb
|
43
59
|
- lib/smart_proxy_acd/version.rb
|
44
60
|
- settings.d/acd.yml.example
|
45
61
|
homepage: http://github.com/ATIX-AG/smart_proxy_acd
|