foreman_ansible 10.2.0 → 10.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: c6ba3f014f5ac02d3364334eb15efeeb9572b802765a9d8ffea9be3d3fd19c3d
4
- data.tar.gz: cc56b4ff89156afee500563975542d1a98d82bc49929d6f2c056fbcc74149500
3
+ metadata.gz: 8c30c3eac6e4a49a8d43c6a654d84b672ef9fa58bbacb8fe2044cc74f33b5a28
4
+ data.tar.gz: 8084c75afc69d788093952a8988e3ff883f3726779a94f1de5016c1b857d7435
5
5
  SHA512:
6
- metadata.gz: 91c980eef1c77f9d856dad9fff7acaca458bb17c5d8736cd50e11c87a2b01ccba5b87c07e025b57e1c4e493b91909607f4236107e99ab419faa66ac00b4b374c
7
- data.tar.gz: 415fda190f9d33d7e31f34567aa4601724b789045e48813f0668a36e58bbf3a7ec414d99bdc8ca97ac9b564f55861b0ae678f1ef11cb197405572b51a52d083e
6
+ metadata.gz: e541db04ed8d5bb3ed65b8239a06366efbda044d57263e4e507534c61a89d566e1270b67593a5c33d423abf13bc7896f76208aa81efc36f58382bd972d40cd7f
7
+ data.tar.gz: 5df022f589352f580358303a3172f8062e476884607e1dafd21ee8f140646e106a93419c018b896df55b4bc4cac102374bb92bd3c46e8dc9e42998c52c2bb26e
@@ -8,12 +8,14 @@ module Types
8
8
  argument :match, String, required: false
9
9
  end
10
10
 
11
- field :meta, ::Types::Meta, resolve: (proc do |object|
11
+ field :meta, ::Types::Meta
12
+
13
+ def meta
12
14
  {
13
15
  :can_edit => ::User.current.can?(object.ansible_variable.permission_name(:edit), object.ansible_variable),
14
16
  :can_destroy => ::User.current.can?(object.ansible_variable.permission_name(:destroy), object.ansible_variable)
15
17
  }
16
- end)
18
+ end
17
19
 
18
20
  def lookup_values(match: nil)
19
21
  return CollectionLoader.for(object.ansible_variable.class, :lookup_values).load(object.ansible_variable) unless match
@@ -3,13 +3,19 @@
3
3
  module ForemanAnsible
4
4
  module AnsibleHostgroupsHelper
5
5
  def ansible_hostgroups_actions(hostgroup)
6
+ actions = []
6
7
  play_roles = if hostgroup.all_ansible_roles.empty?
7
8
  { action: { content: (link_to _('Run all Ansible roles'), 'javascript:void(0);', disabled: true, title: 'No roles assigned', class: 'disabled'), options: { class: 'disabled' } }, priority: 31 }
8
9
  else
9
10
  { action: display_link_if_authorized(_('Run all Ansible roles'), hash_for_play_roles_hostgroup_path(id: hostgroup), 'data-no-turbolink': true, title: _('Run all Ansible roles on hosts belonging to this host group')), priority: 31 }
10
11
  end
11
12
 
12
- [play_roles] if User.current.can?(:create_job_invocations)
13
+ assign_jobs = { action: { content: (link_to _('Configure Ansible Job'), "/ansible/hostgroups/#{hostgroup.id}", class: 'la') }, priority: 32 }
14
+
15
+ actions.push play_roles if User.current.can?(:create_job_invocations)
16
+ actions.push assign_jobs if User.current.can?(:view_job_invocations) && User.current.can?(:view_recurring_logics)
17
+
18
+ actions
13
19
  end
14
20
  end
15
21
  end
@@ -27,25 +27,37 @@ module ForemanAnsible
27
27
 
28
28
  def ansible_module_message(log)
29
29
  msg_json = parsed_message_json(log)
30
- module_action = msg_json['module']
30
+ return _("Execution error: #{msg_json['msg']}") if msg_json['failed'].present?
31
+ return msg_json['censored'] if msg_json['censored'].present?
32
+
33
+ module_action = msg_json.fetch('module', '').delete_prefix('ansible.builtin.').delete_prefix('ansible.legacy.')
31
34
  case module_action
32
35
  when 'package'
33
36
  msg_json['results'].empty? ? msg_json['msg'] : msg_json['results']
34
37
  when 'template'
35
- module_args = msg_json['invocation']['module_args']
36
- _("Rendered template #{module_args['_original_basename']} to #{msg_json['dest']}")
38
+ get_results(msg_json) do |module_args, result|
39
+ _("Rendered template #{module_args['_original_basename']} to #{result['dest']}")
40
+ end
37
41
  when 'service'
38
- _("Service #{msg_json['name']} #{msg_json['state']} (enabled: #{msg_json['enabled']})")
42
+ get_results(msg_json) do |_, result|
43
+ _("Service #{result['name']} #{result['state']} (enabled: #{result['enabled']})")
44
+ end
39
45
  when 'group'
40
- _("User group #{msg_json['name']} #{msg_json['state']}, gid: #{msg_json['gid']}")
46
+ get_results(msg_json) do |_, result|
47
+ _("User group #{result['name']} #{result['state']}, gid: #{result['gid']}")
48
+ end
41
49
  when 'user'
42
- _("User #{msg_json['name']} #{msg_json['state']}, uid: #{msg_json['uid']}")
50
+ get_results(msg_json) do |_, result|
51
+ _("User #{result['name']} #{result['state']}, uid: #{result['uid']}")
52
+ end
43
53
  when 'cron'
44
- module_args = msg_json['invocation']['module_args']
45
- _("Cron job: #{module_args['minute']} #{module_args['hour']} #{module_args['day']} #{module_args['month']} #{module_args['weekday']} #{module_args['job']} (disabled: #{module_args['disabled']})")
54
+ get_results(msg_json) do |module_args, _|
55
+ _("Cron job: #{module_args['minute']} #{module_args['hour']} #{module_args['day']} #{module_args['month']} #{module_args['weekday']} #{module_args['job']} (disabled: #{module_args['disabled']})")
56
+ end
46
57
  when 'copy'
47
- module_args = msg_json['invocation']['module_args']
48
- _("Copy #{module_args['_original_basename']} to #{msg_json['dest']}")
58
+ get_results(msg_json) do |module_args, result|
59
+ _("Copy #{module_args['_original_basename']} to #{result['dest']}")
60
+ end
49
61
  when 'command', 'shell'
50
62
  msg_json['stdout_lines']
51
63
  else
@@ -69,6 +81,14 @@ module ForemanAnsible
69
81
 
70
82
  private
71
83
 
84
+ def get_results(msg_json)
85
+ results = msg_json.key?('results') ? msg_json['results'] : [msg_json]
86
+ results.map do |result|
87
+ module_args = result.fetch('invocation', {}).fetch('module_args', {})
88
+ yield module_args, result
89
+ end
90
+ end
91
+
72
92
  def parsed_message_json(log)
73
93
  JSON.parse(log.message.value)
74
94
  rescue StandardError => e
@@ -1,5 +1,5 @@
1
1
  class FixAnsibleSettingCategoryToDsl < ActiveRecord::Migration[6.0]
2
2
  def up
3
- Setting.where(category: 'Setting::Ansible').update_all(category: 'Setting')
3
+ Setting.where(category: 'Setting::Ansible').update_all(category: 'Setting') if column_exists?(:settings, :category)
4
4
  end
5
5
  end
@@ -4,5 +4,5 @@
4
4
  # This way other parts of Foreman can just call ForemanAnsible::VERSION
5
5
  # and detect what version the plugin is running.
6
6
  module ForemanAnsible
7
- VERSION = '10.2.0'
7
+ VERSION = '10.4.0'
8
8
  end
@@ -1,4 +1,4 @@
1
- {
1
+ [{
2
2
  "reporter": "ansible",
3
3
  "reported_at":"2018-01-15 17:31:36 521275",
4
4
  "metrics": {
@@ -24,4 +24,108 @@
24
24
  }
25
25
  }
26
26
  ]
27
- }
27
+ },
28
+ {
29
+ "reporter": "ansible",
30
+ "reported_at":"2022-12-22 10:52:48 521275",
31
+ "metrics": {
32
+ "time":
33
+ { "total":133 }
34
+ },
35
+ "host": "io.local",
36
+ "status": {
37
+ "applied": 8,
38
+ "failed": 0,
39
+ "skipped": 0
40
+ },
41
+ "logs": [
42
+ {
43
+ "log": {
44
+ "sources": {
45
+ "source": "Schedule multiple cronjobs"
46
+ },
47
+ "messages": {
48
+ "message": "{\"changed\": true, \"failed\": false, \"module\": \"cron\", \"msg\": \"All items completed\", \"results\": [{\"_ansible_item_label\": \"date\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"envs\": [], \"failed\": false, \"invocation\": {\"module_args\": {\"backup\": false, \"cron_file\": null, \"day\": \"*\", \"disabled\": false, \"env\": null, \"hour\": \"5,2\", \"insertafter\": null, \"insertbefore\": null, \"job\": \"date > /dev/null\", \"minute\": \"0\", \"month\": \"*\", \"name\": \"Cron date\", \"reboot\": false, \"special_time\": null, \"state\": \"present\", \"user\": null, \"weekday\": \"*\"}}, \"item\": \"date\", \"jobs\": [\"Cron date\"]}, {\"_ansible_item_label\": \"df\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"envs\": [], \"failed\": false, \"invocation\": {\"module_args\": {\"backup\": false, \"cron_file\": null, \"day\": \"*\", \"disabled\": false, \"env\": null, \"hour\": \"5,2\", \"insertafter\": null, \"insertbefore\": null, \"job\": \"df > /dev/null\", \"minute\": \"0\", \"month\": \"*\", \"name\": \"Cron df\", \"reboot\": false, \"special_time\": null, \"state\": \"present\", \"user\": null, \"weekday\": \"*\"}}, \"item\": \"df\", \"jobs\": [\"Cron date\", \"Cron df\"]}]}"
49
+ },
50
+ "level": "notice"
51
+ }
52
+ },
53
+ {
54
+ "log": {
55
+ "sources": {
56
+ "source": "Schedule one cronjob"
57
+ },
58
+ "messages": {
59
+ "message": "{\"_ansible_no_log\": false, \"changed\": true, \"envs\": [], \"failed\": false, \"invocation\": {\"module_args\": {\"backup\": false, \"cron_file\": null, \"day\": \"*\", \"disabled\": false, \"env\": null, \"hour\": \"5,2\", \"insertafter\": null, \"insertbefore\": null, \"job\": \"hostname > /dev/null\", \"minute\": \"0\", \"month\": \"*\", \"name\": \"Schedule hostname\", \"reboot\": false, \"special_time\": null, \"state\": \"present\", \"user\": null, \"weekday\": \"*\"}}, \"jobs\": [\"Cron date\", \"Cron df\", \"Schedule hostname\"], \"module\": \"cron\"}"
60
+ },
61
+ "level": "notice"
62
+ }
63
+ },
64
+ {
65
+ "log": {
66
+ "sources": {
67
+ "source": "Render multiple templates"
68
+ },
69
+ "messages": {
70
+ "message": "{\"changed\": true, \"failed\": false, \"module\": \"template\", \"msg\": \"All items completed\", \"results\": [{\"_ansible_item_label\": \"test1.txt\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"checksum\": \"dba7673010f19a94af4345453005933fd511bea9\", \"dest\": \"/tmp/test1.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test1.txt.j2\", \"attributes\": null, \"backup\": false, \"checksum\": \"dba7673010f19a94af4345453005933fd511bea9\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test1.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": null, \"local_follow\": null, \"mode\": null, \"owner\": null, \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670606.02-7241-175625077259447/source\", \"unsafe_writes\": false, \"validate\": null}}, \"item\": \"test1.txt\", \"md5sum\": null, \"mode\": \"0644\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670606.02-7241-175625077259447/source\", \"state\": \"file\", \"uid\": 0}, {\"_ansible_item_label\": \"test2.txt\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"checksum\": \"9054fbe0b622c638224d50d20824d2ff6782e308\", \"dest\": \"/tmp/test2.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test2.txt.j2\", \"attributes\": null, \"backup\": false, \"checksum\": \"9054fbe0b622c638224d50d20824d2ff6782e308\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test2.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": null, \"local_follow\": null, \"mode\": null, \"owner\": null, \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670620.49-7241-225254470383476/source\", \"unsafe_writes\": false, \"validate\": null}}, \"item\": \"test2.txt\", \"md5sum\": null, \"mode\": \"0644\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670620.49-7241-225254470383476/source\", \"state\": \"file\", \"uid\": 0}]}"
71
+ },
72
+ "level": "notice"
73
+ }
74
+ },
75
+ {
76
+ "log": {
77
+ "sources": {
78
+ "source": "Render one template"
79
+ },
80
+ "messages": {
81
+ "message": "{\"_ansible_no_log\": false, \"changed\": true, \"checksum\": \"41c5985fc771b6ecfe8feaa99f8fa9b77ac7d6ce\", \"dest\": \"/tmp/test3.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test3.txt.j2\", \"attributes\": null, \"backup\": false, \"checksum\": \"41c5985fc771b6ecfe8feaa99f8fa9b77ac7d6ce\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test3.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": null, \"local_follow\": null, \"mode\": null, \"owner\": null, \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670634.79-7306-243717749452063/source\", \"unsafe_writes\": false, \"validate\": null}}, \"md5sum\": null, \"mode\": \"0644\", \"module\": \"template\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670634.79-7306-243717749452063/source\", \"state\": \"file\", \"uid\": 0}"
82
+ },
83
+ "level": "notice"
84
+ }
85
+ },
86
+ {
87
+ "log": {
88
+ "sources": {
89
+ "source": "Copy multiple local files"
90
+ },
91
+ "messages": {
92
+ "message": "{\"changed\": true, \"failed\": false, \"module\": \"copy\", \"msg\": \"All items completed\", \"results\": [{\"_ansible_item_label\": \"test4.txt\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"checksum\": \"ab2649b7e58f7e32b0c75be95d11e2979399d392\", \"dest\": \"/tmp/test4.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test4.txt\", \"attributes\": null, \"backup\": false, \"checksum\": \"ab2649b7e58f7e32b0c75be95d11e2979399d392\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test4.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": \"root\", \"local_follow\": null, \"mode\": 256, \"owner\": \"root\", \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670648.88-7343-91799014257533/source\", \"unsafe_writes\": false, \"validate\": null}}, \"item\": \"test4.txt\", \"md5sum\": null, \"mode\": \"0400\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670648.88-7343-91799014257533/source\", \"state\": \"file\", \"uid\": 0}, {\"_ansible_item_label\": \"test5.txt\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"checksum\": \"4ea77484f3a1c7dde4c0cca2f5c40953388f19f5\", \"dest\": \"/tmp/test5.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test5.txt\", \"attributes\": null, \"backup\": false, \"checksum\": \"4ea77484f3a1c7dde4c0cca2f5c40953388f19f5\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test5.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": \"root\", \"local_follow\": null, \"mode\": 256, \"owner\": \"root\", \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670662.97-7343-50902792283881/source\", \"unsafe_writes\": false, \"validate\": null}}, \"item\": \"test5.txt\", \"md5sum\": null, \"mode\": \"0400\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670662.97-7343-50902792283881/source\", \"state\": \"file\", \"uid\": 0}]}"
93
+ },
94
+ "level": "notice"
95
+ }
96
+ },
97
+ {
98
+ "log": {
99
+ "sources": {
100
+ "source": "Copy one local files"
101
+ },
102
+ "messages": {
103
+ "message": "{\"_ansible_no_log\": false, \"changed\": true, \"checksum\": \"ec4cddb45c3ce640bed61b3d8ab6c18e715dac78\", \"dest\": \"/tmp/test6.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test6.txt\", \"attributes\": null, \"backup\": false, \"checksum\": \"ec4cddb45c3ce640bed61b3d8ab6c18e715dac78\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test6.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": \"root\", \"local_follow\": null, \"mode\": 256, \"owner\": \"root\", \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670677.05-7408-75605497546833/source\", \"unsafe_writes\": false, \"validate\": null}}, \"md5sum\": null, \"mode\": \"0400\", \"module\": \"copy\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670677.05-7408-75605497546833/source\", \"state\": \"file\", \"uid\": 0}"
104
+ },
105
+ "level": "notice"
106
+ }
107
+ },
108
+ {
109
+ "log": {
110
+ "sources": {
111
+ "source": "Restart multiple services"
112
+ },
113
+ "messages": {
114
+ "message": "{\"changed\":true,\"failed\":false,\"module\":\"service\",\"msg\":\"All items completed\",\"results\":[{\"_ansible_item_label\":\"chronyd\",\"_ansible_no_log\":false,\"ansible_loop_var\":\"item\",\"changed\":true,\"failed\":false,\"invocation\":{\"module_args\":{\"daemon_reexec\":false,\"daemon_reload\":false,\"enabled\":null,\"force\":null,\"masked\":null,\"name\":\"chronyd\",\"no_block\":false,\"scope\":null,\"state\":\"restarted\",\"user\":null}},\"item\":\"chronyd\",\"name\":\"chronyd\",\"state\":\"started\"},{\"_ansible_item_label\":\"firewalld\",\"_ansible_no_log\":false,\"ansible_loop_var\":\"item\",\"changed\":true,\"failed\":false,\"invocation\":{\"module_args\":{\"daemon_reexec\":false,\"daemon_reload\":false,\"enabled\":null,\"force\":null,\"masked\":null,\"name\":\"firewalld\",\"no_block\":false,\"scope\":null,\"state\":\"restarted\",\"user\":null}},\"item\":\"firewalld\",\"name\":\"firewalld\",\"state\":\"started\"}]}"
115
+ },
116
+ "level": "notice"
117
+ }
118
+ },
119
+ {
120
+ "log": {
121
+ "sources": {
122
+ "source": "Restart one service"
123
+ },
124
+ "messages": {
125
+ "message": "{\"_ansible_no_log\":false,\"changed\":true,\"failed\":false,\"invocation\":{\"module_args\":{\"daemon_reexec\":false,\"daemon_reload\":false,\"enabled\":null,\"force\":null,\"masked\":null,\"name\":\"chronyd\",\"no_block\":false,\"scope\":null,\"state\":\"restarted\",\"user\":null}},\"module\":\"service\",\"name\":\"chronyd\",\"state\":\"started\"}"
126
+ },
127
+ "level": "notice"
128
+ }
129
+ }
130
+ ]
131
+ }]
@@ -4,13 +4,15 @@ require 'test_plugin_helper'
4
4
 
5
5
  # Tests for the behavior of Host with roles, checks inheritance, etc
6
6
  class ConfigReportExtensionsTest < ActiveSupport::TestCase
7
- let(:example_report) do
7
+ let(:example_reports) do
8
8
  JSON.parse(File.read(ansible_fixture_file('report.json')))
9
9
  end
10
10
 
11
+ let(:example_report1) { example_reports.first }
12
+
11
13
  describe '.import' do
12
14
  it 'sets an origin for Ansible reports' do
13
- report = ConfigReport.import(example_report)
15
+ report = ConfigReport.import(example_report1)
14
16
  assert_equal 'Ansible', report.origin
15
17
  end
16
18
 
@@ -19,4 +19,64 @@ ANSIBLELOG
19
19
  ansible_module_message(log).to_s
20
20
  )
21
21
  end
22
+
23
+ test 'module message extraction with action' do
24
+ example_report = JSON.parse(File.read(ansible_fixture_file('report.json'))).second
25
+ report = ConfigReport.import(example_report)
26
+ expected_outputs = [
27
+ 'No additional data',
28
+ ['Cron job: 0 5,2 * * * date > /dev/null (disabled: false)', 'Cron job: 0 5,2 * * * df > /dev/null (disabled: false)'],
29
+ ['Cron job: 0 5,2 * * * hostname > /dev/null (disabled: false)'],
30
+ ['Rendered template test1.txt.j2 to /tmp/test1.txt', 'Rendered template test2.txt.j2 to /tmp/test2.txt'],
31
+ ['Rendered template test3.txt.j2 to /tmp/test3.txt'],
32
+ ['Copy test4.txt to /tmp/test4.txt', 'Copy test5.txt to /tmp/test5.txt'],
33
+ ['Copy test6.txt to /tmp/test6.txt'],
34
+ ['Service chronyd started (enabled: )', 'Service firewalld started (enabled: )'],
35
+ ['Service chronyd started (enabled: )']
36
+ ]
37
+ actual_outputs = []
38
+ report.logs.each do |log|
39
+ actual_outputs << ansible_module_message(log)
40
+ end
41
+ assert_equal expected_outputs, actual_outputs
42
+ end
43
+
44
+ test 'accepting an almost empty message' do
45
+ log_value = <<-ANSIBLELOG.strip_heredoc
46
+ {"changed": true, "failed": false, "module": "copy"}
47
+ ANSIBLELOG
48
+ message = FactoryBot.build(:message, value: log_value)
49
+ log = FactoryBot.build(:log)
50
+ log.message = message
51
+ assert_match(
52
+ /Copy/,
53
+ ansible_module_message(log).to_s
54
+ )
55
+ end
56
+
57
+ test 'FQCN module message extraction' do
58
+ log_value = <<-ANSIBLELOG.strip_heredoc
59
+ {"msg": "Nothing to do", "changed": false, "results": [], "rc": 0, "invocation": {"module_args": {"name": ["openssh"], "state": "present", "allow_downgrade": false, "autoremove": false, "bugfix": false, "disable_gpg_check": false, "disable_plugin": [], "disablerepo": [], "download_only": false, "enable_plugin": [], "enablerepo": [], "exclude": [], "installroot": "/", "install_repoquery": true, "install_weak_deps": true, "security": false, "skip_broken": false, "update_cache": false, "update_only": false, "validate_certs": true, "lock_timeout": 30, "conf_file": null, "disable_excludes": null, "download_dir": null, "list": null, "releasever": null}}, "_ansible_no_log": false, "failed": false, "module": "ansible.builtin.package"}
60
+ ANSIBLELOG
61
+ message = FactoryBot.build(:message, value: log_value)
62
+ log = FactoryBot.build(:log)
63
+ log.message = message
64
+ assert_match(
65
+ /Nothing to do/,
66
+ ansible_module_message(log).to_s
67
+ )
68
+ end
69
+
70
+ test 'accepting a censored message' do
71
+ log_value = <<-ANSIBLELOG.strip_heredoc
72
+ {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": true, "failed": false, "module": "copy"}
73
+ ANSIBLELOG
74
+ message = FactoryBot.build(:message, value: log_value)
75
+ log = FactoryBot.build(:log)
76
+ log.message = message
77
+ assert_match(
78
+ /output has been hidden/,
79
+ ansible_module_message(log).to_s
80
+ )
81
+ end
22
82
  end
@@ -8,4 +8,9 @@ fragment CurrentUserAttributes on User {
8
8
  name
9
9
  }
10
10
  }
11
+ usergroups {
12
+ nodes {
13
+ admin
14
+ }
15
+ }
11
16
  }
@@ -11,7 +11,10 @@ export const permissionCheck = (user, permissionsRequired) => {
11
11
  );
12
12
  }
13
13
 
14
- if (user.admin) {
14
+ if (
15
+ user.admin ||
16
+ user.usergroups.nodes.find(usergroup => usergroup.admin === true)
17
+ ) {
15
18
  return { allowed: true };
16
19
  }
17
20
 
@@ -71,6 +71,9 @@ export const userFactory = (login, permissions = []) => ({
71
71
  permissions: {
72
72
  nodes: permissions,
73
73
  },
74
+ usergroups: {
75
+ nodes: [],
76
+ },
74
77
  });
75
78
 
76
79
  export const admin = {
@@ -81,6 +84,9 @@ export const admin = {
81
84
  permissions: {
82
85
  nodes: [],
83
86
  },
87
+ usergroups: {
88
+ nodes: [],
89
+ },
84
90
  };
85
91
 
86
92
  export const intruder = userFactory('intruder', [
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_ansible
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.2.0
4
+ version: 10.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Lobato Garcia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-04 00:00:00.000000000 Z
11
+ date: 2023-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acts_as_list
@@ -467,7 +467,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
467
467
  - !ruby/object:Gem::Version
468
468
  version: '0'
469
469
  requirements: []
470
- rubygems_version: 3.1.6
470
+ rubygems_version: 3.1.4
471
471
  signing_key:
472
472
  specification_version: 4
473
473
  summary: Ansible integration with Foreman (theforeman.org)
@@ -477,34 +477,35 @@ test_files:
477
477
  - test/factories/ansible_variables.rb
478
478
  - test/factories/host_ansible_enhancements.rb
479
479
  - test/fixtures/insights_playbook.yaml
480
- - test/fixtures/report.json
481
- - test/fixtures/sample_facts.json
482
480
  - test/fixtures/playbooks_example_output.json
481
+ - test/fixtures/sample_facts.json
483
482
  - test/fixtures/sample_playbooks.json
483
+ - test/fixtures/report.json
484
484
  - test/foreman_ansible/helpers/ansible_roles_helper_test.rb
485
485
  - test/functional/ansible_roles_controller_test.rb
486
486
  - test/functional/ansible_variables_controller_test.rb
487
487
  - test/functional/api/v2/ansible_inventories_controller_test.rb
488
+ - test/functional/api/v2/ansible_playbooks_controller_test.rb
488
489
  - test/functional/api/v2/ansible_roles_controller_test.rb
489
490
  - test/functional/api/v2/ansible_variables_controller_test.rb
490
491
  - test/functional/api/v2/hostgroups_controller_test.rb
491
492
  - test/functional/api/v2/hosts_controller_test.rb
492
- - test/functional/api/v2/ansible_playbooks_controller_test.rb
493
- - test/functional/ui_ansible_roles_controller_test.rb
494
493
  - test/functional/hosts_controller_test.rb
494
+ - test/functional/ui_ansible_roles_controller_test.rb
495
495
  - test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb
496
496
  - test/graphql/queries/ansible_roles_query_test.rb
497
497
  - test/graphql/queries/host_ansible_roles_query_test.rb
498
498
  - test/test_plugin_helper.rb
499
499
  - test/unit/ansible_role_test.rb
500
500
  - test/unit/ansible_variable_test.rb
501
- - test/unit/concerns/config_reports_extensions_test.rb
502
501
  - test/unit/concerns/host_managed_extensions_test.rb
503
502
  - test/unit/concerns/hostgroup_extensions_test.rb
503
+ - test/unit/concerns/config_reports_extensions_test.rb
504
504
  - test/unit/helpers/ansible_reports_helper_test.rb
505
505
  - test/unit/host_ansible_role_test.rb
506
506
  - test/unit/hostgroup_ansible_role_test.rb
507
507
  - test/unit/ignore_roles_test.rb
508
+ - test/unit/import_playbooks_test.rb
508
509
  - test/unit/import_roles_and_variables.rb
509
510
  - test/unit/lib/proxy_api/ansible_test.rb
510
511
  - test/unit/services/ansible_report_importer_test.rb
@@ -515,6 +516,5 @@ test_files:
515
516
  - test/unit/services/override_resolver_test.rb
516
517
  - test/unit/services/roles_importer_test.rb
517
518
  - test/unit/services/ui_roles_importer_test.rb
518
- - test/unit/import_playbooks_test.rb
519
519
  - test/unit/ansible_provider_test.rb
520
520
  - test/integration/hostgroup_js_test.rb