smart_proxy_acd 0.1.0 → 0.4.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: 80a978c97059f5794f0edf5f62699f12e45a022b3332870159ca4a1c8114576a
4
- data.tar.gz: 65b8540e1cdba88109ab89fda19b0f1dd2bc9efb6efa6e463b562b475814642a
3
+ metadata.gz: 183250a25f2479402b6eb73acd841ec985da00ebe17cb4b836df7b1e3d37ae48
4
+ data.tar.gz: d9b0c52df1fad8c9a998930252617eb971a8319a3f53fdd0e910b17bcef8b439
5
5
  SHA512:
6
- metadata.gz: 9631b51f3a5b85eab8fa4312b2a887a7a5907ba5dc5ec52767c307e21502d386051448be42997dc8be24bcf6784ac010bf04d2f56c35d28eff8a96f8b1521e65
7
- data.tar.gz: bb80129d8c7111f8e9f28002accbc96f48c3863b71439866d3466baa29f8f4ce0907c6af24972b6b16039a87e8cf29e5d1af950f97dadc544395c0a0ec380f2e
6
+ metadata.gz: 88664606e79721dc883ee637bf749f54d6bfd3481ba42366abb4c0725a727fad4ec94d1029b25f0142b889b6bcdbde07f6e1142f419a939e8b3d4ae9ccddc5fa
7
+ data.tar.gz: 23cc133598f5d3888e0298bf5b2c3786d6a429ff6ba3625d673efdc0f61f338e64d749811c8dcbab8ff0cce544d7ec5672c813dbb38492cf2293f16d59c30f33
data/README.md CHANGED
@@ -14,7 +14,6 @@ This plug-in adds support for [Application Centric Deployment](https://github.co
14
14
 
15
15
  We expect your proxy to also have
16
16
  [smart_proxy_dynflow](https://github.com/theforeman/smart_proxy_dynflow)
17
- and [foreman-tasks-core](https://github.com/theforeman/foreman-tasks) as
18
17
  a gem requirement.
19
18
 
20
19
  ### Get the code
@@ -5,8 +5,19 @@ module Proxy
5
5
  # Implement a SmartProxy Plugin
6
6
  class Plugin < ::Proxy::Plugin
7
7
  plugin 'acd', Proxy::Acd::VERSION
8
- http_rackup_path File.expand_path('acd_http_config.ru', File.expand_path('../', __FILE__))
9
- https_rackup_path File.expand_path('acd_http_config.ru', File.expand_path('../', __FILE__))
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
+ default_settings :timeout => 60
17
+
18
+ load_dependency_injection_wirings do |_container_instance, _settings|
19
+ Proxy::Dynflow::TaskLauncherRegistry.register('acd', AcdTaskLauncher)
20
+ end
10
21
  end
11
22
  end
12
23
  end
@@ -5,6 +5,17 @@ module Proxy
5
5
  extend ::Proxy::Log
6
6
 
7
7
  class << self
8
+ def plugin_settings
9
+ # rubocop:disable ClassVars
10
+ @@settings ||= OpenStruct.new(read_settings)
11
+ # rubocop:enable ClassVars
12
+ end
13
+
14
+ def read_settings
15
+ ::Proxy::Acd::Plugin.default_settings.merge(
16
+ YAML.load_file(File.join(::Proxy::SETTINGS.settings_directory, ::Proxy::Acd::Plugin.settings_file))
17
+ )
18
+ end
8
19
  end
9
20
  end
10
21
  end
@@ -0,0 +1,164 @@
1
+ require 'smart_proxy_dynflow/runner/base'
2
+ require 'smart_proxy_dynflow/runner/command_runner'
3
+ require 'tempfile'
4
+ require 'rest-client'
5
+ require 'tmpdir'
6
+ require 'socket'
7
+ require 'proxy/request'
8
+
9
+ # rubocop:disable ClassLength
10
+
11
+ module Proxy
12
+ module Acd
13
+ # Implements the AcdRunner to be used by foreman_remote_execution
14
+ class AcdRunner < Proxy::Dynflow::Runner::CommandRunner
15
+ DEFAULT_REFRESH_INTERVAL = 1
16
+
17
+ def initialize(options, suspended_action:)
18
+ super(options, :suspended_action => suspended_action)
19
+ @options = options
20
+ end
21
+
22
+ def get_playbook(playbook_id)
23
+ logger.debug("Get playbook with id #{playbook_id}")
24
+ response = playbook_resource(playbook_id)
25
+ if response.code.to_s != '200'
26
+ raise "Failed performing callback to Foreman server: #{response.code} #{response.body}"
27
+ end
28
+ tmp_file = Tempfile.new.path
29
+ File.write(tmp_file, response.body)
30
+ @playbook_tmp_base64_file = tmp_file
31
+ end
32
+
33
+ def playbook_resource(playbook_id)
34
+ playbook_download_path = "/acd/api/v2/ansible_playbooks/#{playbook_id}/grab"
35
+ foreman_request = Proxy::HttpRequest::ForemanRequest.new
36
+ req = foreman_request.request_factory.create_get(playbook_download_path)
37
+ foreman_request.http.read_timeout = Proxy::Acd::Plugin.settings.timeout
38
+ foreman_request.send_request(req)
39
+ end
40
+
41
+ def store_playbook
42
+ logger.debug('Unpack ansible playbook')
43
+ dir = Dir.mktmpdir
44
+ raise 'Could not create temporary directory to run ansible playbook' if dir.nil? || !Dir.exist?(dir)
45
+ command = "base64 -d #{@playbook_tmp_base64_file} | tar xz -C #{dir}"
46
+ system(command)
47
+ @playbook_tmp_dir = dir
48
+ end
49
+
50
+ def cleanup
51
+ File.unlink(@playbook_tmp_base64_file) if File.exist?(@playbook_tmp_base64_file)
52
+ FileUtils.rm_rf(@playbook_tmp_dir) if Dir.exist?(@playbook_tmp_dir)
53
+ end
54
+
55
+ def start
56
+ parse_acd_job
57
+
58
+ publish_data("Grab playbook to configure application #{@application_name}...", 'stdout')
59
+ get_playbook(@playbook_id)
60
+ store_playbook
61
+
62
+ @playbook_path = File.join(@playbook_tmp_dir, @playbook_file)
63
+ raise "Could not run playbook: playbook file #{@playbook_file} not found in playbook dir #{@playbook_tmp_dir}" unless File.exist?(@playbook_path)
64
+
65
+ publish_data('Write temporary inventory', 'stdout')
66
+ write_inventory
67
+
68
+ command = generate_command
69
+ logger.debug("Running command #{command.join(' ')}")
70
+ initialize_command(*command)
71
+ end
72
+
73
+ def close
74
+ logger.debug("Cleanup ansible playbook #{@playbook_tmp_dir} and #{@playbook_tmp_base64_file}")
75
+ cleanup
76
+ end
77
+
78
+ def kill
79
+ publish_data('== TASK ABORTED BY USER ==', 'stdout')
80
+ publish_exit_status(1)
81
+ ::Process.kill('SIGTERM', @command_pid)
82
+ end
83
+
84
+ private
85
+
86
+ def parse_acd_job
87
+ @acd_job = YAML.safe_load(@options['script'])
88
+ @application_name = @acd_job['application_name']
89
+ @playbook_id = @acd_job['playbook_id']
90
+ @playbook_file = @acd_job['playbook_file']
91
+
92
+ raise "'playbook_file' need to be specified" if @playbook_file.nil? || @playbook_file.empty?
93
+ raise "'playbook_id' need to be specified" if @playbook_id.nil?
94
+ end
95
+
96
+ def proxy_hostname
97
+ Socket.gethostbyname(Socket.gethostname).first
98
+ end
99
+
100
+ def write_inventory
101
+ complete_inventory = YAML.safe_load(@acd_job['inventory'])
102
+ my_inventory = complete_inventory[proxy_hostname]
103
+
104
+ my_inventory['all']['children'].each_value do |group|
105
+ group['hosts'].each_value do |host_vars|
106
+ host_vars['ansible_ssh_private_key_file'] ||= Proxy::RemoteExecution::Ssh::Plugin.settings[:ssh_identity_key_file]
107
+ end
108
+ end
109
+
110
+ tmp_inventory_file = Tempfile.new('acd_inventory')
111
+ tmp_inventory_file << my_inventory.to_yaml
112
+ tmp_inventory_file << "\n"
113
+ tmp_inventory_file.close
114
+ @inventory_path = tmp_inventory_file.path
115
+ end
116
+
117
+ def environment
118
+ env = {}
119
+ env['ANSIBLE_CALLBACK_WHITELIST'] = ''
120
+ env['ANSIBLE_LOAD_CALLBACK_PLUGINS'] = '0'
121
+ env
122
+ end
123
+
124
+ def setup_verbosity
125
+ verbosity = ''
126
+ if @acd_job['verbose']
127
+ verbosity_level = @acd_job['verbose'].split(' ').first.to_i
128
+ if verbosity_level.positive?
129
+ verbosity = '-'
130
+ verbosity_level.times do
131
+ verbosity += 'v'
132
+ end
133
+ end
134
+ end
135
+ verbosity
136
+ end
137
+
138
+ def valid_tags(tag)
139
+ re = /^\w+(\s*,\s*\w+)*$/
140
+ return true if @acd_job[tag] && @acd_job[tag].match?(re)
141
+ end
142
+
143
+ def generate_command
144
+ logger.debug("Generate command with #{@inventory_path} to run #{@playbook_id} with path #{@playbook_path}")
145
+ command = [environment]
146
+ command << 'ansible-playbook'
147
+ command << '-i'
148
+ command << @inventory_path
149
+ verbose = setup_verbosity
150
+ command << verbose unless verbose.empty?
151
+ command << "--tags '#{@acd_job['tags']}'" if valid_tags('tags')
152
+ command << "--skip-tags '#{@acd_job['skip_tags']}'" if valid_tags('skip_tags')
153
+ if @acd_job.key?('extra_vars') && !@acd_job['extra_vars'].nil? && !@acd_job['extra_vars'].empty?
154
+ command << '--extra-vars'
155
+ command << "'#{@acd_job['extra_vars']}'"
156
+ end
157
+ command << @playbook_path.to_s
158
+ command
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ # rubocop:enable ClassLength
@@ -0,0 +1,27 @@
1
+ require 'smart_proxy_dynflow/task_launcher'
2
+
3
+ module Proxy
4
+ module Acd
5
+ # Implements the TaskLauncher::Batch for Acd
6
+ class AcdTaskLauncher < Proxy::Dynflow::TaskLauncher::Batch
7
+ # Implements the Runner::Action for Acd
8
+ class AcdRunnerAction < Proxy::Dynflow::Action::Runner
9
+ def initiate_runner
10
+ additional_options = {
11
+ :step_id => run_step_id,
12
+ :uuid => execution_plan_id
13
+ }
14
+ ::Proxy::Acd::AcdRunner.new(
15
+ input.merge(additional_options),
16
+ :suspended_action => suspended_action
17
+ )
18
+ end
19
+ end
20
+
21
+ def child_launcher(parent)
22
+ Proxy::Dynflow::TaskLauncher::Single.new(world, callback, :parent => parent,
23
+ :action_class_override => AcdRunnerAction)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  module Proxy
2
2
  module Acd
3
- VERSION = '0.1.0'.freeze
3
+ VERSION = '0.4.0'.freeze
4
4
  end
5
5
  end
@@ -1,2 +1,4 @@
1
1
  ---
2
2
  :enabled: true
3
+ # The time we wait till the request to download the ansible playbook should be answered by the Foreman, in seconds.
4
+ :timeout: 60
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.1.0
4
+ version: 0.4.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-03-09 00:00:00.000000000 Z
11
+ date: 2022-07-15 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