moose-inventory 1.0.9 → 2.1

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.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +15 -1
  3. data/.github/workflows/release.yml +60 -0
  4. data/.gitignore +2 -1
  5. data/.gitleaks.toml +9 -0
  6. data/.rubocop.yml +49 -0
  7. data/BACKLOG.md +752 -24
  8. data/Gemfile +2 -0
  9. data/Gemfile.lock +36 -1
  10. data/README.md +340 -44
  11. data/Rakefile +2 -0
  12. data/bin/moose-inventory +2 -1
  13. data/docs/architecture/architecture-and-trust-boundaries.md +444 -0
  14. data/docs/compatibility/cli-output-compatibility.md +76 -0
  15. data/docs/governance/approval-register.md +37 -0
  16. data/docs/maintenance/database-backup-restore-guidance.md +162 -0
  17. data/docs/maintenance/package-maintenance-and-agent-boundaries.md +260 -0
  18. data/docs/process/conformance-gap-analysis-2026-05-28.md +192 -0
  19. data/docs/product/product-brief.md +161 -0
  20. data/docs/product/requirements-baseline.md +477 -0
  21. data/docs/qa/qa-documentation-and-release-gates.md +283 -0
  22. data/docs/release/package-provenance-hardening.md +126 -0
  23. data/docs/release/publishing.md +54 -50
  24. data/docs/release/release-environment-protection.md +70 -0
  25. data/docs/release/release-readiness.md +37 -4
  26. data/docs/security/accepted-risk-register.md +84 -0
  27. data/docs/security/security-privacy-process.md +287 -0
  28. data/docs/security-audit-2026-05-26-rerun.md +75 -0
  29. data/docs/security-audit-2026-05-26.md +63 -0
  30. data/docs/ux/cli-workflow-notes.md +287 -0
  31. data/examples/ansible/ansible.cfg +3 -0
  32. data/examples/ansible/inventory/moose_inventory.yml +5 -0
  33. data/examples/ansible/inventory_plugins/moose_inventory.py +100 -0
  34. data/examples/ci/README.md +16 -0
  35. data/examples/ci/github-actions/inventory-review.yml +38 -0
  36. data/examples/ci/inventory/example-snapshot.yml +19 -0
  37. data/examples/ci/scripts/validate-inventory-snapshot.sh +30 -0
  38. data/lib/moose_inventory/cli/application.rb +133 -5
  39. data/lib/moose_inventory/cli/association_rendering.rb +74 -0
  40. data/lib/moose_inventory/cli/association_rendering_support.rb +89 -0
  41. data/lib/moose_inventory/cli/audit.rb +62 -0
  42. data/lib/moose_inventory/cli/audit_recording.rb +40 -0
  43. data/lib/moose_inventory/cli/child_relation_rendering.rb +110 -0
  44. data/lib/moose_inventory/cli/console.rb +135 -0
  45. data/lib/moose_inventory/cli/db.rb +64 -0
  46. data/lib/moose_inventory/cli/factory.rb +28 -0
  47. data/lib/moose_inventory/cli/formatter.rb +8 -12
  48. data/lib/moose_inventory/cli/group.rb +7 -1
  49. data/lib/moose_inventory/cli/group_add.rb +91 -73
  50. data/lib/moose_inventory/cli/group_addchild.rb +41 -66
  51. data/lib/moose_inventory/cli/group_addhost.rb +33 -71
  52. data/lib/moose_inventory/cli/group_addvar.rb +27 -47
  53. data/lib/moose_inventory/cli/group_get.rb +8 -42
  54. data/lib/moose_inventory/cli/group_list.rb +7 -40
  55. data/lib/moose_inventory/cli/group_listvars.rb +9 -55
  56. data/lib/moose_inventory/cli/group_rm.rb +105 -73
  57. data/lib/moose_inventory/cli/group_rmchild.rb +47 -57
  58. data/lib/moose_inventory/cli/group_rmhost.rb +34 -61
  59. data/lib/moose_inventory/cli/group_rmvar.rb +30 -41
  60. data/lib/moose_inventory/cli/group_tags.rb +33 -0
  61. data/lib/moose_inventory/cli/helpers.rb +143 -0
  62. data/lib/moose_inventory/cli/host.rb +8 -2
  63. data/lib/moose_inventory/cli/host_add.rb +91 -66
  64. data/lib/moose_inventory/cli/host_addgroup.rb +39 -66
  65. data/lib/moose_inventory/cli/host_addvar.rb +28 -52
  66. data/lib/moose_inventory/cli/host_get.rb +9 -37
  67. data/lib/moose_inventory/cli/host_list.rb +24 -21
  68. data/lib/moose_inventory/cli/host_listvars.rb +9 -62
  69. data/lib/moose_inventory/cli/host_rm.rb +60 -42
  70. data/lib/moose_inventory/cli/host_rmgroup.rb +39 -55
  71. data/lib/moose_inventory/cli/host_rmvar.rb +31 -45
  72. data/lib/moose_inventory/cli/host_tags.rb +33 -0
  73. data/lib/moose_inventory/cli/listvars_support.rb +55 -0
  74. data/lib/moose_inventory/cli/plan_rendering.rb +50 -0
  75. data/lib/moose_inventory/cli/relation_transaction_support.rb +51 -0
  76. data/lib/moose_inventory/cli/tag_support.rb +97 -0
  77. data/lib/moose_inventory/cli/variable_rendering.rb +67 -0
  78. data/lib/moose_inventory/config/config.rb +185 -108
  79. data/lib/moose_inventory/db/db.rb +188 -193
  80. data/lib/moose_inventory/db/exceptions.rb +6 -3
  81. data/lib/moose_inventory/db/models.rb +16 -0
  82. data/lib/moose_inventory/db/schema_migrations.rb +248 -0
  83. data/lib/moose_inventory/inventory_context.rb +116 -0
  84. data/lib/moose_inventory/operations/add_associations.rb +131 -0
  85. data/lib/moose_inventory/operations/add_groups.rb +123 -0
  86. data/lib/moose_inventory/operations/add_hosts.rb +123 -0
  87. data/lib/moose_inventory/operations/add_variables.rb +77 -0
  88. data/lib/moose_inventory/operations/entity_variable_operation_support.rb +46 -0
  89. data/lib/moose_inventory/operations/group_child_relations.rb +125 -0
  90. data/lib/moose_inventory/operations/group_cleanup.rb +70 -0
  91. data/lib/moose_inventory/operations/import_inventory_snapshot.rb +41 -0
  92. data/lib/moose_inventory/operations/inventory_doctor.rb +172 -0
  93. data/lib/moose_inventory/operations/inventory_snapshot.rb +60 -0
  94. data/lib/moose_inventory/operations/inventory_snapshot_applier.rb +112 -0
  95. data/lib/moose_inventory/operations/inventory_snapshot_preview.rb +174 -0
  96. data/lib/moose_inventory/operations/inventory_snapshot_validator.rb +134 -0
  97. data/lib/moose_inventory/operations/operation_event_support.rb +27 -0
  98. data/lib/moose_inventory/operations/query_inventory/base_query.rb +24 -0
  99. data/lib/moose_inventory/operations/query_inventory/group_queries.rb +86 -0
  100. data/lib/moose_inventory/operations/query_inventory/host_queries.rb +106 -0
  101. data/lib/moose_inventory/operations/query_inventory.rb +47 -0
  102. data/lib/moose_inventory/operations/remove_associations.rb +113 -0
  103. data/lib/moose_inventory/operations/remove_groups.rb +79 -0
  104. data/lib/moose_inventory/operations/remove_hosts.rb +68 -0
  105. data/lib/moose_inventory/operations/remove_variables.rb +67 -0
  106. data/lib/moose_inventory/runtime_options.rb +31 -0
  107. data/lib/moose_inventory/version.rb +3 -1
  108. data/lib/moose_inventory.rb +10 -7
  109. data/moose-inventory.gemspec +22 -35
  110. data/scripts/check.sh +3 -0
  111. data/scripts/ci/check_generated_artifacts.sh +41 -0
  112. data/scripts/ci/check_permissions.sh +5 -0
  113. data/scripts/ci/check_rubocop.sh +33 -0
  114. data/scripts/ci/check_secrets.sh +26 -0
  115. data/scripts/ci/check_security.sh +18 -0
  116. data/scripts/ci/install_security_tools.sh +47 -0
  117. data/scripts/files.rb +5 -4
  118. data/scripts/install_dependencies.sh +2 -0
  119. data/spec/examples/ci_examples_spec.rb +37 -0
  120. data/spec/lib/moose_inventory/ansible_plugin_examples_spec.rb +29 -0
  121. data/spec/lib/moose_inventory/cli/application_doctor_spec.rb +50 -0
  122. data/spec/lib/moose_inventory/cli/application_import_export_spec.rb +100 -0
  123. data/spec/lib/moose_inventory/cli/application_spec.rb +25 -15
  124. data/spec/lib/moose_inventory/cli/audit_spec.rb +56 -0
  125. data/spec/lib/moose_inventory/cli/cli_spec.rb +15 -19
  126. data/spec/lib/moose_inventory/cli/console_spec.rb +98 -0
  127. data/spec/lib/moose_inventory/cli/factory_spec.rb +27 -0
  128. data/spec/lib/moose_inventory/cli/formatter_spec.rb +95 -3
  129. data/spec/lib/moose_inventory/cli/group_add_spec.rb +140 -116
  130. data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +89 -35
  131. data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +81 -84
  132. data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +65 -68
  133. data/spec/lib/moose_inventory/cli/group_get_spec.rb +17 -33
  134. data/spec/lib/moose_inventory/cli/group_list_spec.rb +16 -38
  135. data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +33 -40
  136. data/spec/lib/moose_inventory/cli/group_rm_spec.rb +165 -85
  137. data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +100 -30
  138. data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +76 -78
  139. data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +57 -63
  140. data/spec/lib/moose_inventory/cli/group_spec.rb +2 -0
  141. data/spec/lib/moose_inventory/cli/helpers_spec.rb +146 -0
  142. data/spec/lib/moose_inventory/cli/host_add_spec.rb +170 -116
  143. data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +100 -83
  144. data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +92 -74
  145. data/spec/lib/moose_inventory/cli/host_get_spec.rb +14 -33
  146. data/spec/lib/moose_inventory/cli/host_list_spec.rb +41 -33
  147. data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +45 -53
  148. data/spec/lib/moose_inventory/cli/host_rm_spec.rb +66 -48
  149. data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +73 -83
  150. data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +56 -63
  151. data/spec/lib/moose_inventory/cli/host_spec.rb +2 -0
  152. data/spec/lib/moose_inventory/cli/tags_spec.rb +81 -0
  153. data/spec/lib/moose_inventory/config/config_spec.rb +41 -3
  154. data/spec/lib/moose_inventory/db/db_spec.rb +551 -29
  155. data/spec/lib/moose_inventory/db/exceptions_spec.rb +18 -0
  156. data/spec/lib/moose_inventory/db/models_spec.rb +7 -3
  157. data/spec/lib/moose_inventory/db_lifecycle_spec.rb +73 -0
  158. data/spec/lib/moose_inventory/inventory_context_spec.rb +10 -0
  159. data/spec/lib/moose_inventory/operations/add_associations_spec.rb +111 -0
  160. data/spec/lib/moose_inventory/operations/add_groups_spec.rb +80 -0
  161. data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +82 -0
  162. data/spec/lib/moose_inventory/operations/add_variables_spec.rb +103 -0
  163. data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +122 -0
  164. data/spec/lib/moose_inventory/operations/import_inventory_snapshot_spec.rb +226 -0
  165. data/spec/lib/moose_inventory/operations/inventory_doctor_spec.rb +77 -0
  166. data/spec/lib/moose_inventory/operations/inventory_snapshot_spec.rb +50 -0
  167. data/spec/lib/moose_inventory/operations/operation_event_support_spec.rb +78 -0
  168. data/spec/lib/moose_inventory/operations/query_inventory_spec.rb +146 -0
  169. data/spec/lib/moose_inventory/operations/remove_associations_spec.rb +113 -0
  170. data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +78 -0
  171. data/spec/lib/moose_inventory/operations/remove_hosts_spec.rb +55 -0
  172. data/spec/lib/moose_inventory/operations/remove_variables_spec.rb +83 -0
  173. data/spec/shared/shared_config_setup.rb +4 -3
  174. data/spec/spec_helper.rb +50 -40
  175. data/spec/support/cli_harness.rb +33 -0
  176. metadata +163 -35
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
- require_relative './formatter.rb'
4
+ require_relative 'formatter'
5
+ require_relative '../inventory_context'
6
+ require_relative '../operations/add_groups'
3
7
 
4
8
  module Moose
5
9
  module Inventory
@@ -10,84 +14,98 @@ module Moose
10
14
  #==========================
11
15
  desc 'add NAME', 'Add a group NAME to the inventory'
12
16
  option :hosts
13
- # rubocop:disable Metrics/LineLength
14
- def add(*argv) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
15
- # rubocop:enable Metrics/LineLength
16
- if argv.empty?
17
- abort("ERROR: Wrong number of arguments, #{argv.length} for 1 or more.")
17
+ option :dry_run, type: :boolean
18
+ option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
19
+ def add(*argv)
20
+ abort_if_missing_args(argv, 1, '1 or more')
21
+ validate_machine_plan_request!
22
+
23
+ names = normalize_names(argv)
24
+ hosts = csv_option_names(options[:hosts])
25
+
26
+ abort_if_automatic_group(
27
+ names,
28
+ "ERROR: Cannot manually manipulate the automatic group 'ungrouped'\n"
29
+ )
30
+
31
+ result = build_operation(Moose::Inventory::Operations::AddGroups)
32
+ .call(names: names, hosts: hosts, dry_run: options[:dry_run])
33
+ return if machine_plan_output_rendered?(result, command: 'group add')
34
+
35
+ record_audit({ command: 'group add', action: 'add', entity_type: 'group',
36
+ entity_names: names }, result: result, dry_run: options[:dry_run])
37
+ render_add_groups_events(result.events)
38
+ print_warning_summary(result, success_message: 'Succeeded')
39
+ end
40
+
41
+ private
42
+
43
+ def render_add_groups_events(events)
44
+ events.each { |event| render_add_groups_event(event) }
45
+ end
46
+
47
+ def render_add_groups_event(event)
48
+ payload = event.payload
49
+
50
+ return render_add_groups_event_puts(event.type, payload) if puts_event?(event.type)
51
+ return render_add_groups_event_warn(event.type, payload) if warn_event?(event.type)
52
+ return puts 'Dry run complete. No changes applied.' if event.type == :dry_run_summary
53
+
54
+ render_add_groups_event_fmt(event.type, payload)
55
+ end
56
+
57
+ def puts_event?(type)
58
+ type == :group_started
59
+ end
60
+
61
+ def warn_event?(type)
62
+ %i[group_exists host_missing_created association_exists].include?(type)
63
+ end
64
+
65
+ def render_add_groups_event_puts(type, payload)
66
+ puts "Add group '#{payload[:name]}':" if type == :group_started
67
+ end
68
+
69
+ def render_add_groups_event_warn(type, payload)
70
+ case type
71
+ when :group_exists
72
+ fmt.warn "Group '#{payload[:name]}' already exists, skipping creation.\n"
73
+ when :host_missing_created
74
+ fmt.warn "Host '#{payload[:name]}' doesn't exist, but will be created.\n"
75
+ when :association_exists
76
+ fmt.warn(
77
+ "Association {group:#{payload[:group]} <-> host:#{payload[:host]}} " \
78
+ "already exists, skipping creation.\n"
79
+ )
18
80
  end
81
+ end
19
82
 
20
- # Arguments
21
- names = argv.uniq.map(&:downcase)
22
- options[:hosts] = '' if options[:hosts].nil?
23
- hosts = options[:hosts].downcase.split(',').uniq
83
+ def render_add_groups_event_fmt(type, payload)
84
+ return render_add_groups_event_status(type, payload) if status_event?(type)
24
85
 
25
- # sanity
26
- if names.include?('ungrouped')
27
- abort("ERROR: Cannot manually manipulate the automatic group 'ungrouped'\n")
86
+ case type
87
+ when :creating_group
88
+ fmt.puts 2, '- create group...'
89
+ when :adding_association
90
+ fmt.puts 2, "- add association {group:#{payload[:group]} <-> host:#{payload[:host]}}..."
91
+ when :host_creating_now
92
+ fmt.puts 4, '- host doesn\'t exist, creating now...'
93
+ when :removing_automatic_group
94
+ fmt.puts 2, "- remove automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
95
+ when :group_complete
96
+ fmt.puts 2, '- all OK'
28
97
  end
98
+ end
99
+
100
+ def status_event?(type)
101
+ %i[already_exists_skipping ok].include?(type)
102
+ end
29
103
 
30
- # Convenience
31
- db = Moose::Inventory::DB
32
- fmt = Moose::Inventory::Cli::Formatter
33
-
34
- # Transaction
35
- warn_count = 0
36
- db.transaction do # Transaction start
37
- names.each do |name|
38
- # Add the group
39
- puts "Add group '#{name}':"
40
- group = db.models[:group].find(name: name)
41
- hosts_ds = nil
42
- fmt.puts 2, '- create group...'
43
- if group.nil?
44
- group = db.models[:group].create(name: name)
45
- fmt.puts 4, '- OK'
46
- else
47
- warn_count += 1
48
- fmt.warn "Group '#{name}' already exists, skipping creation.\n"
49
- fmt.puts 4, '- already exists, skipping.'
50
- hosts_ds = group.hosts_dataset
51
- fmt.puts 4, '- OK'
52
- end
53
-
54
- # Associate with hosts
55
- hosts.each do |h|
56
- next if h.nil? || h.empty?
57
- fmt.puts 2, "- add association {group:#{name} <-> host:#{h}}..."
58
- host = db.models[:host].find(name: h)
59
- if host.nil?
60
- warn_count += 1
61
- fmt.warn "Host '#{h}' doesn't exist, but will be created.\n"
62
- fmt.puts 4, "- host doesn't exist, creating now..."
63
- host = db.models[:host].create(name: h)
64
- fmt.puts 6, '- OK'
65
- end
66
- if !hosts_ds.nil? && !hosts_ds[name: h].nil?
67
- warn_count += 1
68
- fmt.warn "Association {group:#{name} <-> host:#{h}}"\
69
- " already exists, skipping creation.\n"
70
- fmt.puts 4, '- already exists, skipping.'
71
- else
72
- group.add_host(host)
73
- end
74
- fmt.puts 4, '- OK'
75
-
76
- # Handle the host's automatic 'ungrouped' group
77
- ungrouped = host.groups_dataset[name: 'ungrouped']
78
- next if ungrouped.nil?
79
- fmt.puts 2, '- remove automatic association {group:ungrouped'\
80
- " <-> host:#{h}}..."
81
- host.remove_group(ungrouped) unless ungrouped.nil?
82
- fmt.puts 4, '- OK'
83
- end
84
- fmt.puts 2, '- all OK'
85
- end
86
- end # Transaction end
87
- if warn_count == 0
88
- puts 'Succeeded'
104
+ def render_add_groups_event_status(type, payload)
105
+ if type == :already_exists_skipping
106
+ fmt.puts payload[:indent], '- already exists, skipping.'
89
107
  else
90
- puts 'Succeeded, with warnings.'
108
+ fmt.puts payload[:indent], '- OK'
91
109
  end
92
110
  end
93
111
  end
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
- require_relative './formatter.rb'
4
+ require_relative 'formatter'
5
+ require_relative '../inventory_context'
6
+ require_relative '../operations/group_child_relations'
3
7
 
4
8
  module Moose
5
9
  module Inventory
@@ -10,78 +14,49 @@ module Moose
10
14
  #==========================
11
15
  desc 'addchild PARENTGROUP CHILDGROUP_1 [CHILDGROUP_2 ... ]',
12
16
  'Associate one or more child-groups CHILDGROUP_n with PARENTGROUP'
13
- def addchild(*_argv)
14
- # Sanity check
15
- if args.length < 2
16
- abort("ERROR: Wrong number of arguments, #{args.length} "\
17
- 'for 2 or more.')
18
- end
19
-
20
- # Arguments
21
- pname = args[0].downcase
22
- cnames = args.slice(1, args.length - 1).uniq.map(&:downcase)
23
-
24
- # Sanity
25
- if pname == 'ungrouped' || cnames.include?('ungrouped')
26
- abort("ERROR: Cannot manually manipulate the automatic group 'ungrouped'.")
27
- end
17
+ option :dry_run, type: :boolean
18
+ option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
19
+ def addchild(*argv)
20
+ abort_if_missing_args(argv, 2, '2 or more')
21
+ validate_machine_plan_request!
28
22
 
29
- # Convenience
30
- db = Moose::Inventory::DB
31
- fmt = Moose::Inventory::Cli::Formatter
23
+ pname = argv[0].downcase
24
+ cnames = normalize_names(argv.slice(1, argv.length - 1))
32
25
 
33
- # Transaction
34
- warn_count = 0
35
- begin
36
- db.transaction do # Transaction start
37
- puts "Associate parent group '#{pname}' with child group(s) '#{cnames.join(',')}':"
38
- # Get the target group
39
- fmt.puts 2, "- retrieve group '#{pname}'..."
40
- pgroup = db.models[:group].find(name: pname)
41
- if pgroup.nil?
42
- abort("ERROR: The group '#{pname}' does not exist.")
43
- end
44
- fmt.puts 4, '- OK'
26
+ abort_if_automatic_group([pname] + cnames)
45
27
 
46
- # Associate parent group with the child groups
28
+ result = add_children_to_group(pname, cnames)
29
+ return if machine_plan_output_rendered?(result, command: 'group addchild')
47
30
 
48
- groups_ds = pgroup.children_dataset
49
- cnames.each do |cname|
50
- fmt.puts 2, "- add association {group:#{pname} <-> group:#{cname}}..."
51
-
52
- # Check against existing associations
53
- unless groups_ds[name: cname].nil?
54
- warn_count += 1
55
- fmt.warn "Association {group:#{pname} <-> group:#{cname}}}"\
56
- " already exists, skipping.\n"
57
- fmt.puts 4, '- already exists, skipping.'
58
- fmt.puts 4, '- OK'
59
- next
60
- end
31
+ record_audit({ command: 'group addchild', action: 'associate_child', entity_type: 'group',
32
+ entity_names: pname }, result: result, dry_run: options[:dry_run])
33
+ print_warning_summary(result)
34
+ end
61
35
 
62
- # Add new association
63
- cgroup = db.models[:group].find(name: cname)
64
- if cgroup.nil?
65
- warn_count += 1
66
- fmt.warn "Group '#{cname}' does not exist and will be created.\n"
67
- fmt.puts 4, '- child group does not exist, creating now...'
68
- cgroup = db.models[:group].create(name: cname)
69
- fmt.puts 6, '- OK'
70
- end
71
- pgroup.add_child(cgroup)
72
- fmt.puts 4, '- OK'
73
- end
74
- fmt.puts 2, '- all OK'
75
- end # Transaction end
76
- rescue db.exceptions[:moose] => e
77
- abort("ERROR: #{e}")
78
- end
79
- if warn_count == 0
80
- puts 'Succeeded.'
81
- else
82
- puts 'Succeeded, with warnings.'
36
+ private
37
+
38
+ def add_children_to_group(parent_name, child_names)
39
+ operation = build_operation(Moose::Inventory::Operations::GroupChildRelations)
40
+ run_group_relation_transaction(
41
+ heading: "Associate parent group '#{parent_name}' with child group(s) '#{child_names.join(',')}':",
42
+ on_error: method(:exception_to_s)
43
+ ) do
44
+ parent_group = fetch_existing_group_or_abort(parent_name)
45
+ result = operation.add_children(
46
+ parent_group: parent_group,
47
+ parent_name: parent_name,
48
+ child_names: child_names,
49
+ dry_run: options[:dry_run]
50
+ )
51
+ render_addchild_events(result.events) unless machine_plan_output_requested?
52
+ result
83
53
  end
84
54
  end
55
+
56
+ def render_addchild_events(events)
57
+ emitter = addchild_emitter
58
+ events.each { |event| emitter.call(event) }
59
+ end
85
60
  end
86
61
  end
87
62
  end
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
- require_relative './formatter.rb'
4
+ require_relative 'formatter'
5
+ require_relative '../inventory_context'
6
+ require_relative '../operations/add_associations'
3
7
 
4
8
  module Moose
5
9
  module Inventory
@@ -10,84 +14,42 @@ module Moose
10
14
  #==========================
11
15
  desc 'addhost NAME HOSTNAME',
12
16
  'Associate a host HOSTNAME with the group NAME'
13
- def addhost(*args) # rubocop:disable Metrics/AbcSize
14
- # Sanity
15
- if args.length < 2
16
- abort("ERROR: Wrong number of arguments, #{args.length} "\
17
- 'for 2 or more.')
18
- end
19
-
20
- # Arguments
21
- name = args[0].downcase
22
- hosts = args.slice(1, args.length - 1).uniq.map(&:downcase)
23
-
24
- # Sanity
25
- if name == 'ungrouped'
26
- abort("ERROR: Cannot manually manipulate the automatic group 'ungrouped'.")
27
- end
17
+ option :dry_run, type: :boolean
18
+ option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
19
+ def addhost(*args)
20
+ abort_if_missing_args(args, 2, '2 or more')
21
+ validate_machine_plan_request!
28
22
 
29
- # Convenience
30
- db = Moose::Inventory::DB
31
- fmt = Moose::Inventory::Cli::Formatter
23
+ name = args[0].downcase
24
+ hosts = normalize_names(args.slice(1, args.length - 1))
32
25
 
33
- # Transaction
34
- warn_count = 0
35
- begin
36
- db.transaction do # Transaction start
37
- puts "Associate group '#{name}' with host(s) '#{hosts.join(',')}':"
38
- # Get the target group
39
- fmt.puts 2, "- retrieve group '#{name}'..."
40
- group = db.models[:group].find(name: name)
41
- abort("ERROR: The group '#{name}' does not exist.") if group.nil?
42
- fmt.puts 4, '- OK'
26
+ abort_if_automatic_group([name])
43
27
 
44
- # Associate group with the hosts
45
- ungrouped = db.models[:group].find_or_create(name: 'ungrouped')
46
- hosts_ds = group.hosts_dataset
47
- hosts.each do |h|
48
- fmt.puts 2, "- add association {group:#{name} <-> host:#{h}}..."
28
+ result = add_hosts_to_group(name, hosts)
29
+ return if machine_plan_output_rendered?(result, command: 'group addhost')
49
30
 
50
- # Check against existing associations
51
- unless hosts_ds[name: h].nil?
52
- warn_count += 1
53
- fmt.warn "Association {group:#{name} <-> host:#{h}} already"\
54
- " exists, skipping.\n"
55
- fmt.puts 4, '- already exists, skipping.'
56
- fmt.puts 4, '- OK'
57
- next
58
- end
59
-
60
- # Add new association
61
- host = db.models[:host].find(name: h)
62
- if host.nil?
63
- warn_count += 1
64
- fmt.warn "Host '#{h}' does not exist and will be created.\n"
65
- fmt.puts 4, '- host does not exist, creating now...'
66
- host = db.models[:host].create(name: h)
67
- fmt.puts 6, '- OK'
68
- end
31
+ record_audit({ command: 'group addhost', action: 'associate', entity_type: 'group',
32
+ entity_names: name }, result: result, dry_run: options[:dry_run])
33
+ print_warning_summary(result)
34
+ end
69
35
 
70
- group.add_host(host)
71
- fmt.puts 4, '- OK'
36
+ private
72
37
 
73
- # Remove the host from the ungrouped group, if necessary
74
- next if host.groups_dataset[name: 'ungrouped'].nil?
75
- fmt.puts 2, '- remove automatic association '\
76
- "{group:ungrouped <-> host:#{h}}..."
77
- host.remove_group(ungrouped)
78
- fmt.puts 4, '- OK'
79
- end
80
- fmt.puts 2, '- all OK'
81
- end # Transaction end
82
- rescue db.exceptions[:moose] => e
83
- abort("ERROR: #{e.message}")
84
- end
85
- if warn_count == 0
86
- puts 'Succeeded.'
87
- else
88
- puts 'Succeeded, with warnings.'
38
+ def add_hosts_to_group(name, hosts)
39
+ operation = build_operation(Moose::Inventory::Operations::AddAssociations)
40
+ run_group_relation_transaction(heading: "Associate group '#{name}' with host(s) '#{hosts.join(',')}':") do
41
+ group = fetch_existing_group_or_abort(name)
42
+ result = operation.group_to_hosts(group: group, group_name: name, host_names: hosts,
43
+ dry_run: options[:dry_run])
44
+ render_group_addhost_events(result.events) unless machine_plan_output_requested?
45
+ result
89
46
  end
90
47
  end
48
+
49
+ def render_group_addhost_events(events)
50
+ emitter = host_group_association_addition_emitter(perspective: :group)
51
+ events.each { |event| emitter.call(event) }
52
+ end
91
53
  end
92
54
  end
93
55
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
- require_relative './formatter.rb'
4
+ require_relative '../inventory_context'
5
+ require_relative '../operations/add_variables'
3
6
 
4
7
  module Moose
5
8
  module Inventory
@@ -10,61 +13,38 @@ module Moose
10
13
  #==========================
11
14
  desc 'addvar NAME VARNAME=VALUE',
12
15
  'Add a variable VARNAME with value VALUE to the group NAME'
16
+ option :dry_run, type: :boolean
17
+ option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
13
18
  def addvar(*args)
14
- if args.length < 2
15
- abort('ERROR: Wrong number of arguments, '\
16
- "#{args.length} for 2 or more.")
17
- end
18
- # Convenience
19
- db = Moose::Inventory::DB
20
- fmt = Moose::Inventory::Cli::Formatter
19
+ abort_if_missing_args(args, 2, '2 or more')
20
+ validate_machine_plan_request!
21
21
 
22
- # Arguments
23
22
  name = args[0].downcase
24
23
  vars = args.slice(1, args.length - 1).uniq
24
+ operation = build_operation(Moose::Inventory::Operations::AddVariables,
25
+ entity_type: :group,
26
+ emitter: machine_plan_emitter(group_addvar_emitter(name, vars)))
25
27
 
26
- # Transaction
27
- db.transaction do # Transaction start
28
- puts "Add variables '#{vars.join(',')}' to group '#{name}':"
29
- fmt.puts 2, "- retrieve group '#{name}'..."
30
- group = db.models[:group].find(name: name)
31
- if group.nil?
32
- fail db.exceptions[:moose],
33
- "The group '#{name}' does not exist."
34
- end
35
- fmt.puts 4, '- OK'
28
+ result = db.transaction do
29
+ operation.call(name: name, vars: vars, dry_run: options[:dry_run])
30
+ end
36
31
 
37
- groupvars_ds = group.groupvars_dataset
38
- vars.each do |v|
39
- fmt.puts 2, "- add variable '#{v}'..."
40
- vararray = v.split('=')
32
+ return if machine_plan_output_rendered?(result, command: 'group addvar')
41
33
 
42
- if v.start_with?('=') || v.end_with?('=') || vararray.length != 2
43
- fail db.exceptions[:moose],
44
- "Incorrect format in '{#{v}}'. Expected 'key=value'."
45
- end
34
+ record_audit({ command: 'group addvar', action: 'add_variable', entity_type: 'group',
35
+ entity_names: name }, result: result, dry_run: options[:dry_run])
36
+ print_success_summary
37
+ end
46
38
 
47
- # Check against existing associations
48
- groupvar = groupvars_ds[name: vararray[0]]
49
- if !groupvar.nil?
50
- unless groupvar[:value] == vararray[1]
51
- fmt.puts 4, '- already exists, applying as an update...'
52
- update = db.models[:groupvar].find(id: groupvar[:id])
53
- update[:value] = vararray[1]
54
- update.save
55
- end
56
- else
57
- # groupvar doesn't exist, so create and associate
58
- groupvar = db.models[:groupvar].create(name: vararray[0],
59
- value: vararray[1])
60
- group.add_groupvar(groupvar)
61
- end
62
- fmt.puts 4, '- OK'
63
- end
64
- fmt.puts 2, '- all OK'
65
- end # Transaction end
39
+ private
66
40
 
67
- puts 'Succeeded.'
41
+ def group_addvar_emitter(name, vars)
42
+ variable_operation_emitter(
43
+ action: :add,
44
+ entity_label: 'group',
45
+ entity_name: name,
46
+ variables_label: vars.join(',')
47
+ )
68
48
  end
69
49
  end
70
50
  end
@@ -1,54 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
- require_relative './formatter.rb'
4
+ require_relative '../inventory_context'
5
+ require_relative '../operations/query_inventory'
3
6
 
4
7
  module Moose
5
8
  module Inventory
6
9
  module Cli
7
- ##
8
10
  # Implementation of the "group get" method of the CLI
9
11
  class Group
10
12
  desc 'get GROUP_1 [GROUP_2 ...]', 'Get groups GROUP_n from the inventory'
11
- def get(*argv) # rubocop:disable Metrics/AbcSize
12
- if argv.empty?
13
- abort('ERROR: Wrong number of arguments, '\
14
- "#{argv.length} for 1 or more")
15
- end
16
-
17
- # Convenience
18
- db = Moose::Inventory::DB
19
- fmt = Moose::Inventory::Cli::Formatter
20
-
21
- # Arguments
22
- names = argv.uniq.map(&:downcase)
23
-
24
- # Process
25
- results = {}
26
- names.each do |name|
27
- group = db.models[:group].find(name: name)
28
-
29
- next if group.nil?
30
- hosts = group.hosts_dataset.map(:name)
31
-
32
- children = group.children_dataset.map(:name)
33
-
34
- groupvars = {}
35
- group.groupvars_dataset.each do |gv|
36
- groupvars[gv[:name].to_sym] = gv[:value]
37
- end
38
-
39
- results[group[:name].to_sym] = {}
40
- results[group[:name].to_sym][:hosts] = hosts unless hosts.empty?
41
-
42
- unless children.empty?
43
- results[group[:name].to_sym][:children] = children
44
- end
45
-
46
- unless groupvars.empty?
47
- results[group[:name].to_sym][:groupvars] = groupvars
48
- end
49
- end
13
+ def get(*argv)
14
+ abort("ERROR: Wrong number of arguments, #{argv.length} for 1 or more") if argv.empty?
50
15
 
51
- fmt.dump(results)
16
+ names = normalize_names(argv)
17
+ fmt.dump(inventory_query.get_groups(names: names), output_format)
52
18
  end
53
19
  end
54
20
  end
@@ -1,52 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
- require_relative './formatter.rb'
4
+ require_relative '../inventory_context'
5
+ require_relative '../operations/query_inventory'
3
6
 
4
7
  module Moose
5
8
  module Inventory
6
9
  module Cli
7
- ##
8
10
  # Implementation of the "group list" method of the CLI
9
11
  class Group
10
- #==========================
11
12
  desc 'list',
12
13
  'List the groups, together with any associated hosts and groupvars'
13
- def list # rubocop:disable Metrics/AbcSize
14
- # Convenience
15
- db = Moose::Inventory::DB
16
- confopts = Moose::Inventory::Config._confopts
17
-
18
- # Process
19
- results = {}
20
- db.models[:group].all.each do |group|
21
- hosts = group.hosts_dataset.map(:name)
22
-
23
- # Hide the automatic ungrouped group, if it's empty
24
- next if group[:name] == 'ungrouped' && hosts.empty?
25
-
26
- children = group.children_dataset.map(:name)
27
-
28
- groupvars = {}
29
- group.groupvars_dataset.each do |gv|
30
- groupvars[gv[:name].to_sym] = gv[:value]
31
- end
32
-
33
- results[group[:name].to_sym] = {}
34
- unless hosts.empty? && (confopts[:ansible] != true)
35
- results[group[:name].to_sym][:hosts] = hosts
36
- end
37
-
38
- unless children.empty?
39
- results[group[:name].to_sym][:children] = children
40
- end
41
-
42
- next if groupvars.empty?
43
- if confopts[:ansible] == true
44
- results[group[:name].to_sym][:vars] = groupvars
45
- else
46
- results[group[:name].to_sym][:groupvars] = groupvars
47
- end
48
- end
49
- Formatter.out(results)
14
+ def list
15
+ results = inventory_query.list_groups(ansible: ansible_mode?)
16
+ fmt.dump(results, output_format)
50
17
  end
51
18
  end
52
19
  end