smart_proxy_ansible 2.1.0 → 3.1.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
- SHA1:
3
- metadata.gz: ce925932dfc2ddf578d2ae15b293ee1a223b8808
4
- data.tar.gz: 54f6dd7b4d48229be33aef44b7445d8284a7d734
2
+ SHA256:
3
+ metadata.gz: 723f4dc74c428f9eb0d34bda1f492e8b5e49eb2103f16ccb36b279db8dfb3045
4
+ data.tar.gz: 553c1a6918b5f345d7017fe9ce691bffed6903c7d1e5fdbcbe5905a863e70542
5
5
  SHA512:
6
- metadata.gz: a88b68d82b25d7baeff478651fdf159c512be92262d2c7c5ed5af5ed204915043ab08dbe3d7b3200a7261226f0ece19f2e803be10d3e25e105caa3f6d46bc97e
7
- data.tar.gz: 0c57bf4282a7f4e9699b9108e3e89817a2e2ded17ef448d8d51ae5eeede0a93d8947673d3df034d775fee2238fdb5c2fc40a707c1acc77aa5044953c062c0e97
6
+ metadata.gz: 2bde47200bda54b10495128b81a19f3f6471f1c9142e034537df43e6d8b6badfcf7685a59923d5bc008263231a64d414ab5c6aecb4f0adf3342f3dfb300a35d1
7
+ data.tar.gz: 6793a645db2e1e1ac75d1136cfa5e10285a2fb6b100d97b7a67613e7e4cac504f0154b398ed066f9ea17cb8f65589a8b76df60fece50764a9bb658c9181d145a
data/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  Proxy plugin to make [foreman_ansible](https://github.com/theforeman/foreman_ansible) actions run in the proxy
4
4
 
5
+ ## Compatibility
6
+
7
+ This plugin requires at least Foreman Proxy 2.3.
8
+
5
9
  ## Installation (in development)
6
10
 
7
11
  ### Prerequisites
@@ -4,6 +4,8 @@ module Proxy
4
4
  # please keep the actual implementation of the endpoints outside
5
5
  # of this class.
6
6
  class Api < Sinatra::Base
7
+ include ::Proxy::Log
8
+
7
9
  get '/roles' do
8
10
  RolesReader.list_roles.to_json
9
11
  end
@@ -11,22 +13,38 @@ module Proxy
11
13
  get '/roles/variables' do
12
14
  variables = {}
13
15
  RolesReader.list_roles.each do |role_name|
14
- variables[role_name] = extract_variables(role_name)[role_name]
16
+ variables.merge!(extract_variables(role_name))
17
+ rescue ReadVariablesException => e
18
+ # skip what cannot be parsed
19
+ logger.error e
15
20
  end
16
21
  variables.to_json
17
22
  end
18
23
 
19
24
  get '/roles/:role_name/variables' do |role_name|
20
25
  extract_variables(role_name).to_json
26
+ rescue ReadVariablesException => e
27
+ logger.error e
28
+ {}.to_json
21
29
  end
22
30
 
23
31
  private
24
32
 
25
33
  def extract_variables(role_name)
26
34
  variables = {}
27
- RolesReader.roles_path.split(':').each do |path|
28
- variables[role_name] = VariablesExtractor
29
- .extract_variables("#{path}/#{role_name}")
35
+ role_name_parts = role_name.split('.')
36
+ if role_name_parts.count == 3
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]}")
40
+ end
41
+ else
42
+ RolesReader.roles_path.split(':').each do |path|
43
+ role_path = "#{path}/#{role_name}"
44
+ if File.directory?(role_path)
45
+ variables[role_name] ||= VariablesExtractor.extract_variables(role_path)
46
+ end
47
+ end
30
48
  end
31
49
  variables
32
50
  end
@@ -36,5 +36,6 @@ module Proxy
36
36
 
37
37
  class ReadConfigFileException < Proxy::Ansible::Exception; end
38
38
  class ReadRolesException < Proxy::Ansible::Exception; end
39
+ class ReadVariablesException < Proxy::Ansible::Exception; end
39
40
  end
40
41
  end
@@ -2,13 +2,20 @@ module Proxy
2
2
  module Ansible
3
3
  # Calls for the smart-proxy API to register the plugin
4
4
  class Plugin < Proxy::Plugin
5
- http_rackup_path File.expand_path('http_config.ru',
6
- File.expand_path('../', __FILE__))
7
- https_rackup_path File.expand_path('http_config.ru',
8
- File.expand_path('../', __FILE__))
9
-
5
+ rackup_path File.expand_path('http_config.ru', __dir__)
10
6
  settings_file 'ansible.yml'
11
7
  plugin :ansible, Proxy::Ansible::VERSION
8
+
9
+ 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
18
+ end
12
19
  end
13
20
  end
14
21
  end
@@ -6,19 +6,32 @@ module Proxy
6
6
  class RolesReader
7
7
  class << self
8
8
  DEFAULT_CONFIG_FILE = '/etc/ansible/ansible.cfg'.freeze
9
- DEFAULT_ROLES_PATH = '/etc/ansible/roles'.freeze
9
+ DEFAULT_ROLES_PATH = '/etc/ansible/roles:/usr/share/ansible/roles'.freeze
10
+ DEFAULT_COLLECTIONS_PATHS = '/etc/ansible/collections:/usr/share/ansible/collections'.freeze
10
11
 
11
12
  def list_roles
12
- roles_path.split(':').map { |path| read_roles(path) }.flatten
13
+ roles = roles_path.split(':').map { |path| read_roles(path) }.flatten
14
+ collection_roles = collections_paths.split(':').map { |path| read_collection_roles(path) }.flatten
15
+ roles + collection_roles
13
16
  end
14
17
 
15
- def roles_path(roles_line = roles_path_from_config)
16
- # Default to /etc/ansible/roles if none found
17
- return DEFAULT_ROLES_PATH if roles_line.empty?
18
- roles_path_key = roles_line.first.split('=').first.strip
19
- # In case of commented roles_path key "#roles_path", return default
20
- return DEFAULT_ROLES_PATH unless roles_path_key == 'roles_path'
21
- roles_line.first.split('=').last.strip
18
+ def roles_path
19
+ config_path(path_from_config('roles_path'), DEFAULT_ROLES_PATH)
20
+ end
21
+
22
+ def collections_paths
23
+ config_path(path_from_config('collections_paths'), DEFAULT_COLLECTIONS_PATHS)
24
+ end
25
+
26
+ def config_path(config_line, default)
27
+ # Default to /etc/ansible/roles if config_line is empty
28
+ return default if config_line.empty?
29
+
30
+ config_line_key = config_line.first.split('=').first.strip
31
+ # In case of commented roles_path key "#roles_path" or #collections_paths, return default
32
+ return default if ['#roles_path', '#collections_paths'].include?(config_line_key)
33
+
34
+ config_line.first.split('=').last.strip
22
35
  end
23
36
 
24
37
  def logger
@@ -34,30 +47,43 @@ module Proxy
34
47
  private
35
48
 
36
49
  def read_roles(roles_path)
37
- rescue_and_raise_file_exception ReadRolesException,
38
- roles_path, 'roles' do
39
- Dir.glob("#{roles_path}/*").map do |path|
40
- path.split('/').last
41
- end
50
+ glob_path("#{roles_path}/*").map do |path|
51
+ path.split('/').last
42
52
  end
53
+ rescue Errno::ENOENT, Errno::EACCES => e
54
+ logger.debug(e.backtrace)
55
+ message = "Could not read Ansible roles #{roles_path} - #{e.message}"
56
+ raise ReadRolesException.new(message), message
57
+ end
58
+
59
+ def glob_path(path)
60
+ Dir.glob path
61
+
43
62
  end
44
63
 
45
- def roles_path_from_config
46
- rescue_and_raise_file_exception ReadConfigFileException,
47
- DEFAULT_CONFIG_FILE, 'config file' do
48
- File.readlines(DEFAULT_CONFIG_FILE).select do |line|
49
- line =~ /^\s*roles_path/
50
- end
64
+ def read_collection_roles(collections_path)
65
+ Dir.glob("#{collections_path}/ansible_collections/*/*/roles/*").map do |path|
66
+ parts = path.split('/')
67
+ role = parts.pop
68
+ parts.pop
69
+ collection = parts.pop
70
+ author = parts.pop
71
+ "#{author}.#{collection}.#{role}"
51
72
  end
73
+ rescue Errno::ENOENT, Errno::EACCES => e
74
+ logger.debug(e.backtrace)
75
+ message = "Could not read Ansible roles #{collections_path} - #{e.message}"
76
+ raise ReadRolesException.new(message), message
52
77
  end
53
78
 
54
- def rescue_and_raise_file_exception(exception, path, type)
55
- yield
79
+ def path_from_config(config_key)
80
+ File.readlines(DEFAULT_CONFIG_FILE).select do |line|
81
+ line =~ /^\s*#{config_key}/
82
+ end
56
83
  rescue Errno::ENOENT, Errno::EACCES => e
57
84
  logger.debug(e.backtrace)
58
- exception_message = "Could not read Ansible #{type} "\
59
- "#{path} - #{e.message}"
60
- raise exception.new(exception_message), exception_message
85
+ message = "Could not read Ansible config file #{DEFAULT_CONFIG_FILE} - #{e.message}"
86
+ raise ReadConfigFileException.new(message), message
61
87
  end
62
88
  end
63
89
  end
@@ -1,3 +1,5 @@
1
+ require 'yaml'
2
+
1
3
  module Proxy
2
4
  module Ansible
3
5
  # Implements the logic needed to read the roles and associated information
@@ -6,26 +8,16 @@ module Proxy
6
8
  def extract_variables(role_path)
7
9
  role_files = Dir.glob("#{role_path}/defaults/**/*.yml") +
8
10
  Dir.glob("#{role_path}/defaults/**/*.yaml")
9
- # not anything matching item, }}, {{, ansible_hostname or 'if'
10
- variables = role_files.map do |role_file|
11
- candidates = File.read(role_file)
12
- .scan(/{{(.*?)}}/).select do |param|
13
- param.first.scan(/item/) == [] && param.first.scan(/if/) == []
14
- end.flatten
15
- # Sometimes inside the {{ }} there's a OR condition. In such a case,
16
- # let's split and choose possible variables (variables cannot
17
- # contain parenthesis)
18
-
19
- candidates.map do |variable|
20
- variable.split('|').map(&:strip).select do |var|
21
- !var.include?('(') && # variables are not parenthesis
22
- !var.include?('[') && # variables are not arrays
23
- !var.include?('.') && # variables are not objects
24
- !var.include?("'") # variables are not plain strings
25
- end
26
- end unless candidates.nil?
27
- end.compact.flatten.uniq.map(&:strip)
28
- variables
11
+ role_files.reduce({}) do |memo, role_file|
12
+ loaded_yaml = {}
13
+ begin
14
+ loaded_yaml = YAML.load_file(role_file)
15
+ rescue Psych::SyntaxError
16
+ raise ReadVariablesException.new "#{role_file} is not YAML file"
17
+ end
18
+ raise ReadVariablesException.new "Could not parse YAML file: #{role_file}" unless loaded_yaml.is_a? Hash
19
+ memo.merge loaded_yaml
20
+ end
29
21
  end
30
22
  end
31
23
  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 = '2.1.0'
5
+ VERSION = '3.1.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_ansible
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
@@ -9,50 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-12-06 00:00:00.000000000 Z
12
+ date: 2021-05-17 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: bundler
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: '1.7'
21
- type: :development
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - "~>"
26
- - !ruby/object:Gem::Version
27
- version: '1.7'
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: rake
30
16
  requirement: !ruby/object:Gem::Requirement
31
17
  requirements:
32
18
  - - "~>"
33
19
  - !ruby/object:Gem::Version
34
- version: '10.0'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '10.0'
42
- - !ruby/object:Gem::Dependency
43
- name: minitest
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - "~>"
47
- - !ruby/object:Gem::Version
48
- version: '0'
20
+ version: '13.0'
49
21
  type: :development
50
22
  prerelease: false
51
23
  version_requirements: !ruby/object:Gem::Requirement
52
24
  requirements:
53
25
  - - "~>"
54
26
  - !ruby/object:Gem::Version
55
- version: '0'
27
+ version: '13.0'
56
28
  - !ruby/object:Gem::Dependency
57
29
  name: mocha
58
30
  requirement: !ruby/object:Gem::Requirement
@@ -73,14 +45,14 @@ dependencies:
73
45
  requirements:
74
46
  - - "~>"
75
47
  - !ruby/object:Gem::Version
76
- version: '1'
48
+ version: '3'
77
49
  type: :development
78
50
  prerelease: false
79
51
  version_requirements: !ruby/object:Gem::Requirement
80
52
  requirements:
81
53
  - - "~>"
82
54
  - !ruby/object:Gem::Version
83
- version: '1'
55
+ version: '3'
84
56
  - !ruby/object:Gem::Dependency
85
57
  name: rack-test
86
58
  requirement: !ruby/object:Gem::Requirement
@@ -95,20 +67,6 @@ dependencies:
95
67
  - - "~>"
96
68
  - !ruby/object:Gem::Version
97
69
  version: '0'
98
- - !ruby/object:Gem::Dependency
99
- name: rubocop
100
- requirement: !ruby/object:Gem::Requirement
101
- requirements:
102
- - - '='
103
- - !ruby/object:Gem::Version
104
- version: 0.32.1
105
- type: :development
106
- prerelease: false
107
- version_requirements: !ruby/object:Gem::Requirement
108
- requirements:
109
- - - '='
110
- - !ruby/object:Gem::Version
111
- version: 0.32.1
112
70
  - !ruby/object:Gem::Dependency
113
71
  name: logger
114
72
  requirement: !ruby/object:Gem::Requirement
@@ -163,8 +121,7 @@ extra_rdoc_files:
163
121
  files:
164
122
  - LICENSE
165
123
  - README.md
166
- - bin/json_inventory.sh
167
- - bundler.plugins.d/smart_proxy_ansible.rb
124
+ - bundler.d/ansible.rb
168
125
  - lib/smart_proxy_ansible.rb
169
126
  - lib/smart_proxy_ansible/api.rb
170
127
  - lib/smart_proxy_ansible/exception.rb
@@ -186,15 +143,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
186
143
  requirements:
187
144
  - - ">="
188
145
  - !ruby/object:Gem::Version
189
- version: '0'
146
+ version: '2.5'
190
147
  required_rubygems_version: !ruby/object:Gem::Requirement
191
148
  requirements:
192
149
  - - ">="
193
150
  - !ruby/object:Gem::Version
194
151
  version: '0'
195
152
  requirements: []
196
- rubyforge_project:
197
- rubygems_version: 2.6.8
153
+ rubygems_version: 3.1.2
198
154
  signing_key:
199
155
  specification_version: 4
200
156
  summary: Smart-Proxy Ansible plugin
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env bash
2
- # An inventory script to load the inventory from JSON file.
3
- #
4
-
5
- if [ -z "$JSON_INVENTORY_FILE" ]; then
6
- echo "JSON_INVENTORY_FILE not specified"
7
- exit 1
8
- fi
9
-
10
- cat $JSON_INVENTORY_FILE