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
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'cli/factory'
5
+ require 'operations/query_inventory'
6
+
7
+ RSpec.describe Moose::Inventory::Cli::Factory do
8
+ let(:context) { instance_double('InventoryContext') }
9
+ subject(:factory) { described_class.new(context: context) }
10
+
11
+ it 'builds operations with the shared context' do
12
+ operation_class = class_double('OperationClass')
13
+ operation = instance_double('Operation')
14
+
15
+ expect(operation_class).to receive(:new).with(context: context, emitter: :emit).and_return(operation)
16
+
17
+ expect(factory.operation(operation_class, emitter: :emit)).to eq(operation)
18
+ end
19
+
20
+ it 'memoizes the query inventory wrapper for the shared context' do
21
+ first = factory.query_inventory
22
+ second = factory.query_inventory
23
+
24
+ expect(first).to be_a(Moose::Inventory::Operations::QueryInventory)
25
+ expect(second).to equal(first)
26
+ end
27
+ end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
- # TODO: the usual respond_to? method doesn't seem to work on Thor objects.
4
5
  # Why not? For now, we'll check against instance_methods.
5
6
 
6
7
  RSpec.describe Moose::Inventory::Cli::Formatter do
@@ -33,7 +34,7 @@ RSpec.describe Moose::Inventory::Cli::Formatter do
33
34
  actual = runner { @formatter.out(test, 'json') }
34
35
 
35
36
  desired = { aborted: false, STDOUT: '', STDERR: '' }
36
- desired[:STDOUT] = test.to_json + "\n"
37
+ desired[:STDOUT] = "#{test.to_json}\n"
37
38
 
38
39
  expected(actual, desired)
39
40
  end
@@ -44,7 +45,7 @@ RSpec.describe Moose::Inventory::Cli::Formatter do
44
45
  actual = runner { @formatter.out(test, 'prettyjson') }
45
46
 
46
47
  desired = { aborted: false, STDOUT: '', STDERR: '' }
47
- desired[:STDOUT] = JSON.pretty_generate(test) + "\n"
48
+ desired[:STDOUT] = "#{JSON.pretty_generate(test)}\n"
48
49
 
49
50
  expected(actual, desired)
50
51
  end
@@ -59,5 +60,96 @@ RSpec.describe Moose::Inventory::Cli::Formatter do
59
60
 
60
61
  expected(actual, desired)
61
62
  end
63
+
64
+ it 'info() prints the provided message instead of a literal placeholder' do
65
+ actual = runner { @formatter.info(2, 'hello world') }
66
+
67
+ desired = { aborted: false, STDOUT: '', STDERR: '' }
68
+ desired[:STDOUT] = ' INFO: hello world'
69
+
70
+ expected(actual, desired)
71
+ end
72
+ end
73
+
74
+ describe 'stream helpers' do
75
+ it 'puts() writes indented text to stdout by default' do
76
+ actual = runner { @formatter.puts(2, 'hello world') }
77
+
78
+ desired = { aborted: false, STDOUT: " hello world\n", STDERR: '' }
79
+
80
+ expected(actual, desired)
81
+ end
82
+
83
+ it 'puts() writes indented text to stderr when requested' do
84
+ actual = runner { @formatter.puts(2, 'hello world', 'STDERR') }
85
+
86
+ desired = { aborted: false, STDOUT: '', STDERR: " hello world\n" }
87
+
88
+ expected(actual, desired)
89
+ end
90
+
91
+ it 'puts() aborts on an unknown output stream' do
92
+ actual = runner { @formatter.puts(2, 'hello world', 'BOGUS') }
93
+
94
+ desired = { aborted: true, STDOUT: '', STDERR: "Output stream 'BOGUS' is not known.\n" }
95
+
96
+ expected(actual, desired)
97
+ end
98
+
99
+ it 'print() writes indented text to stdout by default' do
100
+ actual = runner { @formatter.print(2, 'hello world') }
101
+
102
+ desired = { aborted: false, STDOUT: ' hello world', STDERR: '' }
103
+
104
+ expected(actual, desired)
105
+ end
106
+
107
+ it 'print() writes indented text to stderr when requested' do
108
+ actual = runner { @formatter.print(2, 'hello world', 'STDERR') }
109
+
110
+ desired = { aborted: false, STDOUT: '', STDERR: ' hello world' }
111
+
112
+ expected(actual, desired)
113
+ end
114
+
115
+ it 'print() aborts on an unknown output stream' do
116
+ actual = runner { @formatter.print(2, 'hello world', 'BOGUS') }
117
+
118
+ desired = { aborted: true, STDOUT: '', STDERR: "Output stream 'BOGUS' is not known.\n" }
119
+
120
+ expected(actual, desired)
121
+ end
122
+
123
+ it 'info() writes to stderr when requested' do
124
+ actual = runner { @formatter.info(2, 'hello world', 'STDERR') }
125
+
126
+ desired = { aborted: false, STDOUT: '', STDERR: ' INFO: hello world' }
127
+
128
+ expected(actual, desired)
129
+ end
130
+
131
+ it 'info() aborts on an unknown output stream' do
132
+ actual = runner { @formatter.info(2, 'hello world', 'BOGUS') }
133
+
134
+ desired = { aborted: true, STDOUT: '', STDERR: "Output stream 'BOGUS' is not known.\n" }
135
+
136
+ expected(actual, desired)
137
+ end
138
+
139
+ it 'warn() writes to stderr with the warning prefix' do
140
+ actual = runner { @formatter.warn('hello world') }
141
+
142
+ desired = { aborted: false, STDOUT: '', STDERR: 'WARNING: hello world' }
143
+
144
+ expected(actual, desired)
145
+ end
146
+
147
+ it 'error() writes to stderr with the error prefix' do
148
+ actual = runner { @formatter.error('hello world') }
149
+
150
+ desired = { aborted: false, STDOUT: '', STDERR: 'ERROR: hello world' }
151
+
152
+ expected(actual, desired)
153
+ end
62
154
  end
63
155
  end
@@ -1,52 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/BlockLength
1
4
  require 'spec_helper'
2
5
 
3
- # TODO: the usual respond_to? method doesn't seem to work on Thor objects.
4
6
  # Why not? For now, we'll check against instance_methods.
5
7
 
6
8
  RSpec.describe Moose::Inventory::Cli::Group do
7
9
  before(:all) do
8
- # Set up the configuration object
9
- @mockarg_parts = {
10
- config: File.join(spec_root, 'config/config.yml'),
11
- format: 'yaml',
12
- env: 'test',
13
- }
14
-
15
- @mockargs = []
16
- @mockarg_parts.each do |key, val|
17
- @mockargs << "--#{key}"
18
- @mockargs << val
19
- end
20
- @mockargs << '--trace' # extra info for debugging
21
-
22
- @console = Moose::Inventory::Cli::Formatter
23
-
24
- @config = Moose::Inventory::Config
25
- @config.init(@mockargs)
26
-
27
- @db = Moose::Inventory::DB
28
- @db.init if @db.db.nil?
29
-
30
- @host = Moose::Inventory::Cli::Host
31
- @group = Moose::Inventory::Cli::Group
32
- @app = Moose::Inventory::Cli::Application
10
+ setup_cli_harness(
11
+ command_class: Moose::Inventory::Cli::Group,
12
+ command_ivar: :@group,
13
+ extra_commands: { :@host => Moose::Inventory::Cli::Host },
14
+ extra_args: ['--trace']
15
+ )
33
16
  end
34
17
 
35
18
  before(:each) do
36
- @db.reset
19
+ reset_cli_harness
37
20
  end
38
21
 
39
22
  # ============================
40
23
  describe 'add' do
41
24
  # --------------------
42
25
  it 'Group.add() method should be responsive' do
43
- result = @group.instance_methods(false).include?(:add)
26
+ result = @group.method_defined?(:add, false)
44
27
  expect(result).to eq(true)
45
28
  end
46
29
 
47
30
  # --------------------
48
31
  it '<no arguments> ... should bail with an error' do
49
- actual = runner { @app.start(%w(group add)) }
32
+ actual = runner { @app.start(%w[group add]) }
50
33
 
51
34
  desired = { aborted: true }
52
35
  desired[:STDERR] = "ERROR: Wrong number of arguments, 0 for 1 or more.\n"
@@ -55,7 +38,7 @@ RSpec.describe Moose::Inventory::Cli::Group do
55
38
 
56
39
  # --------------------
57
40
  it 'ungrouped ... should abort with an error' do
58
- actual = runner { @app.start(%w(group add ungrouped)) }
41
+ actual = runner { @app.start(%w[group add ungrouped]) }
59
42
 
60
43
  # Check output
61
44
  desired = { aborted: true }
@@ -67,17 +50,17 @@ RSpec.describe Moose::Inventory::Cli::Group do
67
50
  # --------------------
68
51
  it 'GROUP ... should add a group to the db' do
69
52
  name = 'test'
70
- actual = runner { @app.start(%W(group add #{name})) }
53
+ actual = runner { @app.start(%W[group add #{name}]) }
71
54
 
72
55
  # @console.out(actual)
73
56
 
74
57
  # Check output
75
58
  desired = {}
76
59
  desired[:STDOUT] =
77
- "Add group '#{name}':\n"\
78
- " - create group...\n"\
79
- " - OK\n"\
80
- " - all OK\n"\
60
+ "Add group '#{name}':\n " \
61
+ "- create group...\n " \
62
+ "- OK\n " \
63
+ "- all OK\n" \
81
64
  "Succeeded\n"
82
65
 
83
66
  expected(actual, desired)
@@ -87,23 +70,68 @@ RSpec.describe Moose::Inventory::Cli::Group do
87
70
  expect(group[:name]).to eq(name)
88
71
  end
89
72
 
73
+ # --------------------
74
+ it 'GROUP --dry-run should show planned group creation without writing to the db' do
75
+ name = 'dry-run-group'
76
+
77
+ actual = runner { @app.start(%W[group add #{name} --dry-run]) }
78
+
79
+ desired = {}
80
+ desired[:STDOUT] =
81
+ "Add group '#{name}':\n " \
82
+ "- create group...\n " \
83
+ "- OK\n " \
84
+ "- all OK\n" \
85
+ "Dry run complete. No changes applied.\n" \
86
+ "Succeeded\n"
87
+
88
+ expected(actual, desired)
89
+ expect(@db.models[:group].find(name: name)).to be_nil
90
+ end
91
+
92
+ # --------------------
93
+ it 'GROUP --hosts HOST --dry-run should not create the group or missing host' do
94
+ group_name = 'dry-run-group'
95
+ host_name = 'dry-run-host'
96
+
97
+ actual = runner { @app.start(%W[group add #{group_name} --hosts #{host_name} --dry-run]) }
98
+
99
+ desired = {}
100
+ desired[:STDOUT] =
101
+ "Add group '#{group_name}':\n " \
102
+ "- create group...\n " \
103
+ "- OK\n " \
104
+ "- add association {group:#{group_name} <-> host:#{host_name}}...\n " \
105
+ "- host doesn't exist, creating now...\n " \
106
+ "- OK\n " \
107
+ "- OK\n " \
108
+ "- all OK\n" \
109
+ "Dry run complete. No changes applied.\n" \
110
+ "Succeeded, with warnings.\n"
111
+ desired[:STDERR] =
112
+ "WARNING: Host '#{host_name}' doesn't exist, but will be created.\n"
113
+
114
+ expected(actual, desired)
115
+ expect(@db.models[:group].find(name: group_name)).to be_nil
116
+ expect(@db.models[:host].find(name: host_name)).to be_nil
117
+ end
90
118
  # --------------------
91
119
  it 'GROUP ... should skip GROUP creation if it already exists' do
92
120
  name = 'test-group'
93
121
  @db.models[:group].create(name: name)
94
122
 
95
- actual = runner { @app.start(%W(group add #{name})) }
123
+ actual = runner { @app.start(%W[group add #{name}]) }
96
124
 
97
125
  # @console.out(actual)
98
126
 
99
127
  # Check output
100
128
  desired = {}
101
129
  desired[:STDOUT] =
102
- "Add group '#{name}':\n"\
103
- " - create group...\n"\
104
- " - already exists, skipping.\n"\
105
- " - OK\n"\
106
- " - all OK\n"\
130
+ "Add group '#{name}':\n " \
131
+ "- create group...\n " \
132
+ "- already exists, skipping.\n " \
133
+ "- OK\n " \
134
+ "- all OK\n" \
107
135
  "Succeeded, with warnings.\n"
108
136
  desired[:STDERR] =
109
137
  "WARNING: Group '#{name}' already exists, skipping creation.\n"
@@ -115,9 +143,9 @@ RSpec.describe Moose::Inventory::Cli::Group do
115
143
 
116
144
  # --------------------
117
145
  it 'GROUP1 GROUP2 GROUP3 ... should add multiple groups' do
118
- names = %w(test1 test2 test3)
146
+ names = %w[test1 test2 test3]
119
147
 
120
- actual = runner { @app.start(%w(group add) + names) }
148
+ actual = runner { @app.start(%w[group add] + names) }
121
149
 
122
150
  # @console.out(actual)
123
151
 
@@ -125,12 +153,12 @@ RSpec.describe Moose::Inventory::Cli::Group do
125
153
  desired = { STDOUT: '' }
126
154
  names.each do |name|
127
155
  desired[:STDOUT] = desired[:STDOUT] +
128
- "Add group '#{name}':\n"\
129
- " - create group...\n"\
130
- " - OK\n"\
131
- " - all OK\n"\
156
+ "Add group '#{name}':\n " \
157
+ "- create group...\n " \
158
+ "- OK\n " \
159
+ "- all OK\n" \
132
160
  end
133
- desired[:STDOUT] = desired[:STDOUT] + "Succeeded\n"
161
+ desired[:STDOUT] = "#{desired[:STDOUT]}Succeeded\n"
134
162
 
135
163
  expected(actual, desired)
136
164
 
@@ -142,15 +170,14 @@ RSpec.describe Moose::Inventory::Cli::Group do
142
170
  end
143
171
 
144
172
  # --------------------
145
- it 'GROUP1 --hosts HOST1 ... should add the '\
146
- 'group and associate it with existing hosts' do
147
-
173
+ it 'GROUP1 --hosts HOST1 ... should add the ' \
174
+ 'group and associate it with existing hosts' do
148
175
  host_name = 'test-host'
149
176
  @db.models[:host].create(name: host_name)
150
177
 
151
178
  group_name = 'test-group'
152
179
  actual = runner do
153
- @app.start(%W(group add #{group_name} --hosts #{host_name}))
180
+ @app.start(%W[group add #{group_name} --hosts #{host_name}])
154
181
  end
155
182
 
156
183
  # @console.out(actual)
@@ -158,12 +185,12 @@ RSpec.describe Moose::Inventory::Cli::Group do
158
185
  # Check output
159
186
  desired = {}
160
187
  desired[:STDOUT] =
161
- "Add group '#{group_name}':\n"\
162
- " - create group...\n"\
163
- " - OK\n"\
164
- " - add association {group:#{group_name} <-> host:#{host_name}}...\n"\
165
- " - OK\n"\
166
- " - all OK\n"\
188
+ "Add group '#{group_name}':\n " \
189
+ "- create group...\n " \
190
+ "- OK\n " \
191
+ "- add association {group:#{group_name} <-> host:#{host_name}}...\n " \
192
+ "- OK\n " \
193
+ "- all OK\n" \
167
194
  "Succeeded\n"
168
195
 
169
196
  expected(actual, desired)
@@ -184,7 +211,7 @@ RSpec.describe Moose::Inventory::Cli::Group do
184
211
 
185
212
  group_name = 'test-group'
186
213
  actual = runner do
187
- @app.start(%W(group add #{group_name} --hosts #{host_name}))
214
+ @app.start(%W[group add #{group_name} --hosts #{host_name}])
188
215
  end
189
216
 
190
217
  # @console.out(actual)
@@ -192,14 +219,14 @@ RSpec.describe Moose::Inventory::Cli::Group do
192
219
  # Check output
193
220
  desired = {}
194
221
  desired[:STDOUT] =
195
- "Add group '#{group_name}':\n"\
196
- " - create group...\n"\
197
- " - OK\n"\
198
- " - add association {group:#{group_name} <-> host:#{host_name}}...\n"\
199
- " - host doesn't exist, creating now...\n"\
200
- " - OK\n"\
201
- " - OK\n"\
202
- " - all OK\n"\
222
+ "Add group '#{group_name}':\n " \
223
+ "- create group...\n " \
224
+ "- OK\n " \
225
+ "- add association {group:#{group_name} <-> host:#{host_name}}...\n " \
226
+ "- host doesn't exist, creating now...\n " \
227
+ "- OK\n " \
228
+ "- OK\n " \
229
+ "- all OK\n" \
203
230
  "Succeeded, with warnings.\n"
204
231
  desired[:STDERR] =
205
232
  "WARNING: Host '#{host_name}' doesn't exist, but will be created.\n"
@@ -220,11 +247,11 @@ RSpec.describe Moose::Inventory::Cli::Group do
220
247
  group_name = 'test-group'
221
248
 
222
249
  # Create group and association
223
- runner { @app.start(%W(group add #{group_name} --hosts #{host_name})) }
250
+ runner { @app.start(%W[group add #{group_name} --hosts #{host_name}]) }
224
251
 
225
252
  # Do it again, to prove that we skip
226
253
  actual = runner do
227
- @app.start(%W(group add #{group_name} --hosts #{host_name}))
254
+ @app.start(%W[group add #{group_name} --hosts #{host_name}])
228
255
  end
229
256
 
230
257
  # @console.out(actual, 'y')
@@ -232,17 +259,17 @@ RSpec.describe Moose::Inventory::Cli::Group do
232
259
  # Check output
233
260
  desired = {}
234
261
  desired[:STDOUT] =
235
- "Add group '#{group_name}':\n"\
236
- " - create group...\n"\
237
- " - already exists, skipping.\n"\
238
- " - OK\n"\
239
- " - add association {group:#{group_name} <-> host:#{host_name}}...\n"\
240
- " - already exists, skipping.\n"\
241
- " - OK\n"\
242
- " - all OK\n"\
262
+ "Add group '#{group_name}':\n " \
263
+ "- create group...\n " \
264
+ "- already exists, skipping.\n " \
265
+ "- OK\n " \
266
+ "- add association {group:#{group_name} <-> host:#{host_name}}...\n " \
267
+ "- already exists, skipping.\n " \
268
+ "- OK\n " \
269
+ "- all OK\n" \
243
270
  "Succeeded, with warnings.\n"
244
271
  desired[:STDERR] =
245
- "WARNING: Group '#{group_name}' already exists, skipping creation.\n"\
272
+ "WARNING: Group '#{group_name}' already exists, skipping creation.\n" \
246
273
  "WARNING: Association {group:#{group_name} <-> host:#{host_name}} already exists, skipping creation.\n"
247
274
 
248
275
  expected(actual, desired)
@@ -256,26 +283,25 @@ RSpec.describe Moose::Inventory::Cli::Group do
256
283
  end
257
284
 
258
285
  # --------------------
259
- it 'GROUP --hosts HOST1,HOST2 ... should add the group and '\
260
- 'associate it with multiple hosts' do
261
-
286
+ it 'GROUP --hosts HOST1,HOST2 ... should add the group and ' \
287
+ 'associate it with multiple hosts' do
262
288
  # The group should be added
263
289
  # Each host should be associated with the group
264
290
  # Each host should be removed from the automatic 'ungrouped' group
265
291
 
266
292
  group_name = 'test-group'
267
- host_names = %w(host1 host2 host3)
293
+ host_names = %w[host1 host2 host3]
268
294
 
269
295
  # Add just the first host. This ensure that we cover paths for both
270
296
  # and existing host (with an 'ungrouped' association) and for none
271
297
  # existing groups.
272
- tmp = runner { @app.start(%W(host add #{host_names[0]})) }
298
+ runner { @app.start(%W[host add #{host_names[0]}]) }
273
299
 
274
300
  # @console.out(tmp, 'y')
275
301
 
276
302
  # Now run the actual group addition
277
303
  actual = runner do
278
- @app.start(%W(group add #{group_name} --hosts #{host_names.join(',')}))
304
+ @app.start(%W[group add #{group_name} --hosts #{host_names.join(',')}])
279
305
  end
280
306
 
281
307
  # @console.out(actual,'y')
@@ -283,25 +309,25 @@ RSpec.describe Moose::Inventory::Cli::Group do
283
309
  # Check output
284
310
  desired = { aborted: false, STDERR: '', STDOUT: '' }
285
311
  desired[:STDOUT] =
286
- "Add group '#{group_name}':\n"\
287
- " - create group...\n"\
288
- " - OK\n"\
289
- " - add association {group:#{group_name} <-> host:#{host_names[0]}}...\n"\
290
- " - OK\n"\
291
- " - remove automatic association {group:ungrouped <-> host:#{host_names[0]}}...\n"\
292
- " - OK\n"
312
+ "Add group '#{group_name}':\n " \
313
+ "- create group...\n " \
314
+ "- OK\n " \
315
+ "- add association {group:#{group_name} <-> host:#{host_names[0]}}...\n " \
316
+ "- OK\n " \
317
+ "- remove automatic association {group:ungrouped <-> host:#{host_names[0]}}...\n " \
318
+ "- OK\n"
293
319
 
294
320
  host_names.slice(1, host_names.length - 1).each do |host_name|
295
321
  desired[:STDOUT] = desired[:STDOUT] +
296
- " - add association {group:#{group_name} <-> host:#{host_name}}...\n"\
297
- " - host doesn't exist, creating now...\n"\
298
- " - OK\n"\
299
- " - OK\n"
322
+ " - add association {group:#{group_name} <-> host:#{host_name}}...\n " \
323
+ "- host doesn't exist, creating now...\n " \
324
+ "- OK\n " \
325
+ "- OK\n"
300
326
  desired[:STDERR] = desired[:STDERR] +
301
327
  "WARNING: Host '#{host_name}' doesn't exist, but will be created.\n"
302
328
  end
303
329
  desired[:STDOUT] = desired[:STDOUT] +
304
- " - all OK\n"\
330
+ " - all OK\n" \
305
331
  "Succeeded, with warnings.\n"
306
332
 
307
333
  expected(actual, desired)
@@ -318,13 +344,13 @@ RSpec.describe Moose::Inventory::Cli::Group do
318
344
  end
319
345
 
320
346
  # --------------------
321
- it 'HOST --groups GROUP1,ungrouped ... should bail '\
322
- 'with an error' do
347
+ it 'HOST --groups GROUP1,ungrouped ... should bail ' \
348
+ 'with an error' do
323
349
  name = 'testhost'
324
- group_names = %w(group1 ungrouped)
350
+ group_names = %w[group1 ungrouped]
325
351
 
326
352
  actual = runner do
327
- @app.start(%W(host add #{name} --groups #{group_names.join(',')}))
353
+ @app.start(%W[host add #{name} --groups #{group_names.join(',')}])
328
354
  end
329
355
 
330
356
  # Check output
@@ -336,15 +362,14 @@ RSpec.describe Moose::Inventory::Cli::Group do
336
362
  end
337
363
 
338
364
  # --------------------
339
- it 'HOST1 HOST2 --groups GROUP1,GROUP2 ... should add multiple '\
340
- 'groups, associating each with multiple hosts' do
341
- #
342
- host_names = %w(host1 host2 host3)
365
+ it 'HOST1 HOST2 --groups GROUP1,GROUP2 ... should add multiple ' \
366
+ 'groups, associating each with multiple hosts' do
367
+ host_names = %w[host1 host2 host3]
343
368
  # Note, relies on auto-generation of hosts
344
369
 
345
- group_names = %w(group1 group2 group3)
370
+ group_names = %w[group1 group2 group3]
346
371
  actual = runner do
347
- @app.start(%w(group add) + group_names + %W(--hosts #{host_names.join(',')}))
372
+ @app.start(%w[group add] + group_names + %W[--hosts #{host_names.join(',')}])
348
373
  end
349
374
 
350
375
  # @console.out(actual,'y')
@@ -354,27 +379,25 @@ RSpec.describe Moose::Inventory::Cli::Group do
354
379
  first_pass = true
355
380
  group_names.each do |group|
356
381
  desired[:STDOUT] = desired[:STDOUT] +
357
- "Add group '#{group}':\n"\
358
- " - create group...\n"\
359
- " - OK\n"
382
+ "Add group '#{group}':\n " \
383
+ "- create group...\n " \
384
+ "- OK\n"
360
385
 
361
386
  host_names.each do |host|
362
387
  desired[:STDOUT] = desired[:STDOUT] +
363
388
  " - add association {group:#{group} <-> host:#{host}}...\n"
364
389
  if first_pass
365
390
  desired[:STDOUT] = desired[:STDOUT] +
366
- " - host doesn't exist, creating now...\n"\
367
- " - OK\n"
391
+ " - host doesn't exist, creating now...\n " \
392
+ "- OK\n"
368
393
  end
369
- desired[:STDOUT] = desired[:STDOUT] +
370
- " - OK\n"
394
+ desired[:STDOUT] = "#{desired[:STDOUT]} - OK\n"
371
395
  end
372
- desired[:STDOUT] = desired[:STDOUT] +
373
- " - all OK\n"
396
+ desired[:STDOUT] = "#{desired[:STDOUT]} - all OK\n"
374
397
  first_pass = false
375
398
  end
376
399
 
377
- desired[:STDOUT] = desired[:STDOUT] + "Succeeded, with warnings.\n"
400
+ desired[:STDOUT] = "#{desired[:STDOUT]}Succeeded, with warnings.\n"
378
401
  host_names.each do |host|
379
402
  desired[:STDERR] = desired[:STDERR] +
380
403
  "WARNING: Host '#{host}' doesn't exist, but will be created.\n"
@@ -395,3 +418,4 @@ RSpec.describe Moose::Inventory::Cli::Group do
395
418
  end
396
419
  end
397
420
  end
421
+ # rubocop:enable Metrics/BlockLength