moose-inventory 2.0 → 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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +2 -0
  3. data/.gitignore +2 -1
  4. data/.rubocop.yml +21 -0
  5. data/BACKLOG.md +630 -8
  6. data/Gemfile +2 -0
  7. data/Gemfile.lock +1 -1
  8. data/README.md +315 -39
  9. data/Rakefile +2 -0
  10. data/bin/moose-inventory +2 -1
  11. data/docs/architecture/architecture-and-trust-boundaries.md +444 -0
  12. data/docs/compatibility/cli-output-compatibility.md +76 -0
  13. data/docs/governance/approval-register.md +37 -0
  14. data/docs/maintenance/database-backup-restore-guidance.md +162 -0
  15. data/docs/maintenance/package-maintenance-and-agent-boundaries.md +260 -0
  16. data/docs/process/conformance-gap-analysis-2026-05-28.md +192 -0
  17. data/docs/product/product-brief.md +161 -0
  18. data/docs/product/requirements-baseline.md +477 -0
  19. data/docs/qa/qa-documentation-and-release-gates.md +283 -0
  20. data/docs/release/package-provenance-hardening.md +126 -0
  21. data/docs/release/publishing.md +11 -3
  22. data/docs/release/release-environment-protection.md +70 -0
  23. data/docs/release/release-readiness.md +23 -4
  24. data/docs/security/accepted-risk-register.md +84 -0
  25. data/docs/security/security-privacy-process.md +287 -0
  26. data/docs/security-audit-2026-05-26-rerun.md +2 -2
  27. data/docs/ux/cli-workflow-notes.md +287 -0
  28. data/examples/ansible/ansible.cfg +3 -0
  29. data/examples/ansible/inventory/moose_inventory.yml +5 -0
  30. data/examples/ansible/inventory_plugins/moose_inventory.py +100 -0
  31. data/examples/ci/README.md +16 -0
  32. data/examples/ci/github-actions/inventory-review.yml +38 -0
  33. data/examples/ci/inventory/example-snapshot.yml +19 -0
  34. data/examples/ci/scripts/validate-inventory-snapshot.sh +30 -0
  35. data/lib/moose_inventory/cli/application.rb +133 -5
  36. data/lib/moose_inventory/cli/association_rendering.rb +74 -0
  37. data/lib/moose_inventory/cli/association_rendering_support.rb +89 -0
  38. data/lib/moose_inventory/cli/audit.rb +62 -0
  39. data/lib/moose_inventory/cli/audit_recording.rb +40 -0
  40. data/lib/moose_inventory/cli/child_relation_rendering.rb +110 -0
  41. data/lib/moose_inventory/cli/console.rb +135 -0
  42. data/lib/moose_inventory/cli/db.rb +64 -0
  43. data/lib/moose_inventory/cli/factory.rb +28 -0
  44. data/lib/moose_inventory/cli/formatter.rb +8 -12
  45. data/lib/moose_inventory/cli/group.rb +5 -2
  46. data/lib/moose_inventory/cli/group_add.rb +11 -9
  47. data/lib/moose_inventory/cli/group_addchild.rb +23 -65
  48. data/lib/moose_inventory/cli/group_addhost.rb +16 -67
  49. data/lib/moose_inventory/cli/group_addvar.rb +27 -47
  50. data/lib/moose_inventory/cli/group_get.rb +8 -42
  51. data/lib/moose_inventory/cli/group_list.rb +7 -40
  52. data/lib/moose_inventory/cli/group_listvars.rb +9 -55
  53. data/lib/moose_inventory/cli/group_rm.rb +12 -10
  54. data/lib/moose_inventory/cli/group_rmchild.rb +26 -82
  55. data/lib/moose_inventory/cli/group_rmhost.rb +18 -53
  56. data/lib/moose_inventory/cli/group_rmvar.rb +30 -41
  57. data/lib/moose_inventory/cli/group_tags.rb +33 -0
  58. data/lib/moose_inventory/cli/helpers.rb +68 -1
  59. data/lib/moose_inventory/cli/host.rb +6 -3
  60. data/lib/moose_inventory/cli/host_add.rb +69 -29
  61. data/lib/moose_inventory/cli/host_addgroup.rb +22 -58
  62. data/lib/moose_inventory/cli/host_addvar.rb +28 -52
  63. data/lib/moose_inventory/cli/host_get.rb +9 -37
  64. data/lib/moose_inventory/cli/host_list.rb +24 -21
  65. data/lib/moose_inventory/cli/host_listvars.rb +9 -62
  66. data/lib/moose_inventory/cli/host_rm.rb +60 -42
  67. data/lib/moose_inventory/cli/host_rmgroup.rb +25 -44
  68. data/lib/moose_inventory/cli/host_rmvar.rb +31 -45
  69. data/lib/moose_inventory/cli/host_tags.rb +33 -0
  70. data/lib/moose_inventory/cli/listvars_support.rb +55 -0
  71. data/lib/moose_inventory/cli/plan_rendering.rb +50 -0
  72. data/lib/moose_inventory/cli/relation_transaction_support.rb +51 -0
  73. data/lib/moose_inventory/cli/tag_support.rb +97 -0
  74. data/lib/moose_inventory/cli/variable_rendering.rb +67 -0
  75. data/lib/moose_inventory/config/config.rb +185 -108
  76. data/lib/moose_inventory/db/db.rb +170 -195
  77. data/lib/moose_inventory/db/exceptions.rb +6 -3
  78. data/lib/moose_inventory/db/models.rb +16 -0
  79. data/lib/moose_inventory/db/schema_migrations.rb +248 -0
  80. data/lib/moose_inventory/inventory_context.rb +68 -2
  81. data/lib/moose_inventory/operations/add_associations.rb +20 -16
  82. data/lib/moose_inventory/operations/add_groups.rb +21 -13
  83. data/lib/moose_inventory/operations/add_hosts.rb +30 -17
  84. data/lib/moose_inventory/operations/add_variables.rb +77 -0
  85. data/lib/moose_inventory/operations/entity_variable_operation_support.rb +46 -0
  86. data/lib/moose_inventory/operations/group_child_relations.rb +23 -16
  87. data/lib/moose_inventory/operations/group_cleanup.rb +23 -8
  88. data/lib/moose_inventory/operations/import_inventory_snapshot.rb +41 -0
  89. data/lib/moose_inventory/operations/inventory_doctor.rb +172 -0
  90. data/lib/moose_inventory/operations/inventory_snapshot.rb +60 -0
  91. data/lib/moose_inventory/operations/inventory_snapshot_applier.rb +112 -0
  92. data/lib/moose_inventory/operations/inventory_snapshot_preview.rb +174 -0
  93. data/lib/moose_inventory/operations/inventory_snapshot_validator.rb +134 -0
  94. data/lib/moose_inventory/operations/operation_event_support.rb +27 -0
  95. data/lib/moose_inventory/operations/query_inventory/base_query.rb +24 -0
  96. data/lib/moose_inventory/operations/query_inventory/group_queries.rb +86 -0
  97. data/lib/moose_inventory/operations/query_inventory/host_queries.rb +106 -0
  98. data/lib/moose_inventory/operations/query_inventory.rb +47 -0
  99. data/lib/moose_inventory/operations/remove_associations.rb +30 -18
  100. data/lib/moose_inventory/operations/remove_groups.rb +12 -12
  101. data/lib/moose_inventory/operations/remove_hosts.rb +68 -0
  102. data/lib/moose_inventory/operations/remove_variables.rb +67 -0
  103. data/lib/moose_inventory/runtime_options.rb +31 -0
  104. data/lib/moose_inventory/version.rb +3 -1
  105. data/lib/moose_inventory.rb +10 -7
  106. data/moose-inventory.gemspec +19 -35
  107. data/scripts/check.sh +1 -0
  108. data/scripts/ci/check_generated_artifacts.sh +41 -0
  109. data/scripts/ci/check_permissions.sh +2 -0
  110. data/scripts/ci/check_rubocop.sh +30 -25
  111. data/scripts/files.rb +5 -4
  112. data/spec/examples/ci_examples_spec.rb +37 -0
  113. data/spec/lib/moose_inventory/ansible_plugin_examples_spec.rb +29 -0
  114. data/spec/lib/moose_inventory/cli/application_doctor_spec.rb +50 -0
  115. data/spec/lib/moose_inventory/cli/application_import_export_spec.rb +100 -0
  116. data/spec/lib/moose_inventory/cli/application_spec.rb +25 -15
  117. data/spec/lib/moose_inventory/cli/audit_spec.rb +56 -0
  118. data/spec/lib/moose_inventory/cli/cli_spec.rb +15 -19
  119. data/spec/lib/moose_inventory/cli/console_spec.rb +98 -0
  120. data/spec/lib/moose_inventory/cli/factory_spec.rb +27 -0
  121. data/spec/lib/moose_inventory/cli/formatter_spec.rb +95 -3
  122. data/spec/lib/moose_inventory/cli/group_add_spec.rb +140 -116
  123. data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +89 -35
  124. data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +81 -84
  125. data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +65 -68
  126. data/spec/lib/moose_inventory/cli/group_get_spec.rb +17 -33
  127. data/spec/lib/moose_inventory/cli/group_list_spec.rb +16 -38
  128. data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +33 -40
  129. data/spec/lib/moose_inventory/cli/group_rm_spec.rb +136 -96
  130. data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +66 -41
  131. data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +76 -78
  132. data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +57 -63
  133. data/spec/lib/moose_inventory/cli/group_spec.rb +2 -0
  134. data/spec/lib/moose_inventory/cli/helpers_spec.rb +146 -0
  135. data/spec/lib/moose_inventory/cli/host_add_spec.rb +170 -116
  136. data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +100 -83
  137. data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +92 -74
  138. data/spec/lib/moose_inventory/cli/host_get_spec.rb +14 -33
  139. data/spec/lib/moose_inventory/cli/host_list_spec.rb +41 -33
  140. data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +45 -53
  141. data/spec/lib/moose_inventory/cli/host_rm_spec.rb +66 -48
  142. data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +73 -83
  143. data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +56 -63
  144. data/spec/lib/moose_inventory/cli/host_spec.rb +2 -0
  145. data/spec/lib/moose_inventory/cli/tags_spec.rb +81 -0
  146. data/spec/lib/moose_inventory/config/config_spec.rb +41 -3
  147. data/spec/lib/moose_inventory/db/db_spec.rb +396 -36
  148. data/spec/lib/moose_inventory/db/exceptions_spec.rb +18 -0
  149. data/spec/lib/moose_inventory/db/models_spec.rb +7 -3
  150. data/spec/lib/moose_inventory/db_lifecycle_spec.rb +73 -0
  151. data/spec/lib/moose_inventory/inventory_context_spec.rb +10 -0
  152. data/spec/lib/moose_inventory/operations/add_associations_spec.rb +34 -0
  153. data/spec/lib/moose_inventory/operations/add_groups_spec.rb +15 -0
  154. data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +13 -0
  155. data/spec/lib/moose_inventory/operations/add_variables_spec.rb +103 -0
  156. data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +46 -0
  157. data/spec/lib/moose_inventory/operations/import_inventory_snapshot_spec.rb +226 -0
  158. data/spec/lib/moose_inventory/operations/inventory_doctor_spec.rb +77 -0
  159. data/spec/lib/moose_inventory/operations/inventory_snapshot_spec.rb +50 -0
  160. data/spec/lib/moose_inventory/operations/operation_event_support_spec.rb +78 -0
  161. data/spec/lib/moose_inventory/operations/query_inventory_spec.rb +146 -0
  162. data/spec/lib/moose_inventory/operations/remove_associations_spec.rb +35 -0
  163. data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +21 -0
  164. data/spec/lib/moose_inventory/operations/remove_hosts_spec.rb +55 -0
  165. data/spec/lib/moose_inventory/operations/remove_variables_spec.rb +83 -0
  166. data/spec/shared/shared_config_setup.rb +4 -3
  167. data/spec/spec_helper.rb +50 -40
  168. data/spec/support/cli_harness.rb +33 -0
  169. metadata +80 -41
@@ -1,15 +1,17 @@
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
10
  # Set up the configuration object
9
11
  @mockarg_parts = {
10
- config: File.join(spec_root, 'config/config.yml'),
11
- format: 'yaml',
12
- env: 'test',
12
+ config: File.join(spec_root, 'config/config.yml'),
13
+ format: 'yaml',
14
+ env: 'test'
13
15
  }
14
16
 
15
17
  @mockargs = []
@@ -39,14 +41,14 @@ RSpec.describe Moose::Inventory::Cli::Group do
39
41
  describe 'rmchild' do
40
42
  #------------------------
41
43
  it 'Group.rmchild() should be responsive' do
42
- result = @group.instance_methods(false).include?(:rmchild)
44
+ result = @group.method_defined?(:rmchild, false)
43
45
  expect(result).to eq(true)
44
46
  end
45
47
 
46
48
  #------------------------
47
49
  it '<missing args> ... should abort with an error' do
48
50
  actual = runner do
49
- @app.start(%w(group addchild))
51
+ @app.start(%w[group addchild])
50
52
  end
51
53
 
52
54
  # @console.out(actual, 'y')
@@ -63,7 +65,7 @@ RSpec.describe Moose::Inventory::Cli::Group do
63
65
  child_name = 'fake'
64
66
 
65
67
  actual = runner do
66
- @app.start(%W(group addchild #{parent_name} #{child_name}))
68
+ @app.start(%W[group addchild #{parent_name} #{child_name}])
67
69
  end
68
70
 
69
71
  # @console.out(actual, 'y')
@@ -79,7 +81,7 @@ RSpec.describe Moose::Inventory::Cli::Group do
79
81
  child_name = 'ungrouped'
80
82
 
81
83
  actual = runner do
82
- @app.start(%W(group rmchild #{parent_name} #{child_name}))
84
+ @app.start(%W[group rmchild #{parent_name} #{child_name} --yes])
83
85
  end
84
86
 
85
87
  # @console.out(actual, 'y')
@@ -96,17 +98,17 @@ RSpec.describe Moose::Inventory::Cli::Group do
96
98
  cname = 'child group'
97
99
 
98
100
  actual = runner do
99
- @app.start(%W(group rmchild #{pname} #{cname}))
101
+ @app.start(%W[group rmchild #{pname} #{cname} --yes])
100
102
  end
101
103
 
102
104
  # @console.out(actual, 'y')
103
105
  # Check output
104
106
  desired = { aborted: true }
105
107
  desired[:STDOUT] =
106
- "Dissociate parent group '#{pname}' from child group(s) '#{cname}':\n"\
107
- " - retrieve group '#{pname}'...\n"
108
+ "Dissociate parent group '#{pname}' from child group(s) '#{cname}':\n " \
109
+ "- retrieve group '#{pname}'...\n"
108
110
  desired[:STDERR] =
109
- "ERROR: The group '#{pname}' does not exist.\n"\
111
+ "ERROR: The group '#{pname}' does not exist.\n" \
110
112
  "An error occurred during a transaction, any changes have been rolled back.\n"
111
113
  expected(actual, desired)
112
114
  end
@@ -116,10 +118,10 @@ RSpec.describe Moose::Inventory::Cli::Group do
116
118
  pname = 'parent_group'
117
119
  cname = 'child group'
118
120
 
119
- runner { @app.start(%W(group add #{pname} #{cname})) }
121
+ runner { @app.start(%W[group add #{pname} #{cname}]) }
120
122
 
121
123
  actual = runner do
122
- @app.start(%W(group rmchild #{pname} #{cname}))
124
+ @app.start(%W[group rmchild #{pname} #{cname} --yes])
123
125
  end
124
126
 
125
127
  # @console.out(actual, 'y')
@@ -127,13 +129,13 @@ RSpec.describe Moose::Inventory::Cli::Group do
127
129
  # Check output
128
130
  desired = {}
129
131
  desired[:STDOUT] =
130
- "Dissociate parent group '#{pname}' from child group(s) '#{cname}':\n"\
131
- " - retrieve group '#{pname}'...\n"\
132
- " - OK\n"\
133
- " - remove association {group:#{pname} <-> group:#{cname}}...\n"\
134
- " - doesn't exist, skipping.\n"\
135
- " - OK\n"\
136
- " - all OK\n"\
132
+ "Dissociate parent group '#{pname}' from child group(s) '#{cname}':\n " \
133
+ "- retrieve group '#{pname}'...\n " \
134
+ "- OK\n " \
135
+ "- remove association {group:#{pname} <-> group:#{cname}}...\n " \
136
+ "- doesn't exist, skipping.\n " \
137
+ "- OK\n " \
138
+ "- all OK\n" \
137
139
  "Succeeded, with warnings.\n"
138
140
 
139
141
  desired[:STDERR] =
@@ -147,11 +149,11 @@ RSpec.describe Moose::Inventory::Cli::Group do
147
149
  pname = 'parent_group'
148
150
  cname = 'child group'
149
151
 
150
- runner { @app.start(%W(group add #{pname} #{cname})) }
151
- runner { @app.start(%W(group addchild #{pname} #{cname})) }
152
+ runner { @app.start(%W[group add #{pname} #{cname}]) }
153
+ runner { @app.start(%W[group addchild #{pname} #{cname}]) }
152
154
 
153
155
  actual = runner do
154
- @app.start(%W(group rmchild #{pname} #{cname}))
156
+ @app.start(%W[group rmchild #{pname} #{cname} --yes])
155
157
  end
156
158
 
157
159
  # @console.out(actual, 'y')
@@ -159,27 +161,49 @@ RSpec.describe Moose::Inventory::Cli::Group do
159
161
  # Check output
160
162
  desired = {}
161
163
  desired[:STDOUT] =
162
- "Dissociate parent group '#{pname}' from child group(s) '#{cname}':\n"\
163
- " - retrieve group '#{pname}'...\n"\
164
- " - OK\n"\
165
- " - remove association {group:#{pname} <-> group:#{cname}}...\n"\
166
- " - OK\n"\
167
- " - all OK\n"\
164
+ "Dissociate parent group '#{pname}' from child group(s) '#{cname}':\n " \
165
+ "- retrieve group '#{pname}'...\n " \
166
+ "- OK\n " \
167
+ "- remove association {group:#{pname} <-> group:#{cname}}...\n " \
168
+ "- OK\n " \
169
+ "- all OK\n" \
168
170
  "Succeeded.\n"
169
171
 
170
172
  expected(actual, desired)
171
173
  end
172
174
 
175
+ #------------------------
176
+ it 'GROUP CHILDGROUP --delete-orphans --dry-run should not remove or delete groups' do
177
+ runner { @app.start(%w[group add parent]) }
178
+ runner { @app.start(%w[group add child --hosts child-host]) }
179
+ runner { @app.start(%w[group addchild parent child]) }
180
+
181
+ actual = runner do
182
+ @app.start(%w[group rmchild --delete-orphans parent child --dry-run])
183
+ end
184
+
185
+ expect(actual[:unexpected]).to eq(false)
186
+ expect(actual[:aborted]).to eq(false)
187
+ expect(actual[:STDOUT]).to include('Dry run complete. No changes applied.')
188
+ expect(actual[:STDOUT]).to include("- Recursively delete orphaned group 'child'...
189
+ ")
190
+ parent = @db.models[:group].find(name: 'parent')
191
+ expect(parent.children_dataset[name: 'child']).not_to be_nil
192
+ expect(@db.models[:group].find(name: 'child')).not_to be_nil
193
+ host = @db.models[:host].find(name: 'child-host')
194
+ expect(host.groups_dataset[name: 'ungrouped']).to be_nil
195
+ end
196
+
173
197
  #------------------------
174
198
  it 'GROUP CHILDGROUP --delete-orphans ... should delete orphaned child groups recursively' do
175
- runner { @app.start(%w(group add parent)) }
176
- runner { @app.start(%w(group add child --hosts child-host)) }
177
- runner { @app.start(%w(group add grandchild)) }
178
- runner { @app.start(%w(group addchild parent child)) }
179
- runner { @app.start(%w(group addchild child grandchild)) }
199
+ runner { @app.start(%w[group add parent]) }
200
+ runner { @app.start(%w[group add child --hosts child-host]) }
201
+ runner { @app.start(%w[group add grandchild]) }
202
+ runner { @app.start(%w[group addchild parent child]) }
203
+ runner { @app.start(%w[group addchild child grandchild]) }
180
204
 
181
205
  actual = runner do
182
- @app.start(%w(group rmchild --delete-orphans parent child))
206
+ @app.start(%w[group rmchild --delete-orphans parent child --yes])
183
207
  end
184
208
 
185
209
  expect(actual[:unexpected]).to eq(false)
@@ -188,7 +212,7 @@ RSpec.describe Moose::Inventory::Cli::Group do
188
212
  expect(actual[:STDOUT]).to include("- Recursively delete orphaned group 'grandchild'...\n")
189
213
 
190
214
  expect(@db.models[:group].find(name: 'parent')).not_to be_nil
191
- %w(child grandchild).each do |name|
215
+ %w[child grandchild].each do |name|
192
216
  expect(@db.models[:group].find(name: name)).to be_nil
193
217
  end
194
218
 
@@ -198,12 +222,12 @@ RSpec.describe Moose::Inventory::Cli::Group do
198
222
 
199
223
  #------------------------
200
224
  it 'GROUP CHILDGROUP --delete-orphans ... should preserve child groups with another parent' do
201
- runner { @app.start(%w(group add parent other-parent)) }
202
- runner { @app.start(%w(group addchild parent child)) }
203
- runner { @app.start(%w(group addchild other-parent child)) }
225
+ runner { @app.start(%w[group add parent other-parent]) }
226
+ runner { @app.start(%w[group addchild parent child]) }
227
+ runner { @app.start(%w[group addchild other-parent child]) }
204
228
 
205
229
  actual = runner do
206
- @app.start(%w(group rmchild --delete-orphans parent child))
230
+ @app.start(%w[group rmchild --delete-orphans parent child --yes])
207
231
  end
208
232
 
209
233
  expect(actual[:unexpected]).to eq(false)
@@ -216,3 +240,4 @@ RSpec.describe Moose::Inventory::Cli::Group do
216
240
  end
217
241
  end
218
242
  end
243
+ # rubocop:enable Metrics/BlockLength
@@ -1,44 +1,28 @@
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
-
21
- @console = Moose::Inventory::Cli::Formatter
22
- @config = Moose::Inventory::Config
23
- @config.init(@mockargs)
24
-
25
- @db = Moose::Inventory::DB
26
- @db.init if @db.db.nil?
27
-
28
- @group = Moose::Inventory::Cli::Group
29
- @host = Moose::Inventory::Cli::Host
30
- @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
+ )
31
15
  end
32
16
 
33
17
  before(:each) do
34
- @db.reset
18
+ reset_cli_harness
35
19
  end
36
20
 
37
21
  #====================
38
22
  describe 'rmgroup' do
39
23
  #----------------
40
24
  it 'should be responsive' do
41
- result = @group.instance_methods(false).include?(:rmhost)
25
+ result = @group.method_defined?(:rmhost, false)
42
26
  expect(result).to eq(true)
43
27
  end
44
28
 
@@ -47,7 +31,7 @@ RSpec.describe Moose::Inventory::Cli::Group do
47
31
  #------------------------
48
32
  it '<missing args> ... should abort with an error' do
49
33
  actual = runner do
50
- @app.start(%w(group rmhost)) # <- no group or hosts given
34
+ @app.start(%w[group rmhost]) # <- no group or hosts given
51
35
  end
52
36
 
53
37
  # Check output
@@ -61,16 +45,16 @@ RSpec.describe Moose::Inventory::Cli::Group do
61
45
  group_name = 'not-a-group'
62
46
  host_name = 'example'
63
47
  actual = runner do
64
- @app.start(%W(group rmhost #{group_name} #{host_name}))
48
+ @app.start(%W[group rmhost #{group_name} #{host_name} --yes])
65
49
  end
66
50
 
67
51
  # Check output
68
52
  desired = { aborted: true }
69
53
  desired[:STDOUT] =
70
- "Dissociate group '#{group_name}' from host(s) '#{host_name}':\n"\
71
- " - retrieve group '#{group_name}'...\n"
54
+ "Dissociate group '#{group_name}' from host(s) '#{host_name}':\n " \
55
+ "- retrieve group '#{group_name}'...\n"
72
56
  desired[:STDERR] =
73
- "ERROR: The group '#{group_name}' does not exist.\n"\
57
+ "ERROR: The group '#{group_name}' does not exist.\n" \
74
58
  "An error occurred during a transaction, any changes have been rolled back.\n"
75
59
  expected(actual, desired)
76
60
  end
@@ -80,9 +64,9 @@ RSpec.describe Moose::Inventory::Cli::Group do
80
64
  host_name = 'test1'
81
65
  group_name = 'group1'
82
66
 
83
- runner { @app.start(%W(host add #{host_name})) }
84
- runner { @app.start(%W(group add #{group_name})) }
85
- runner { @app.start(%W(group addhost #{group_name} #{host_name})) }
67
+ runner { @app.start(%W[host add #{host_name}]) }
68
+ runner { @app.start(%W[group add #{group_name}]) }
69
+ runner { @app.start(%W[group addhost #{group_name} #{host_name}]) }
86
70
 
87
71
  #
88
72
  # Dissociate the host
@@ -90,32 +74,48 @@ RSpec.describe Moose::Inventory::Cli::Group do
90
74
  # 2. expect that no association with ungrouped is made.
91
75
 
92
76
  actual = runner do
93
- @app.start(%W(group rmhost #{group_name} #{host_name}))
77
+ @app.start(%W[group rmhost #{group_name} #{host_name} --yes])
94
78
  end
95
79
 
96
80
  # @console.dump(actual, 'y')
97
81
 
98
- # rubocop:disable Metrics/LineLength
99
82
  desired = { aborted: false }
100
83
  desired[:STDOUT] =
101
- "Dissociate group '#{group_name}' from host(s) '#{host_name}':\n"\
102
- " - retrieve group '#{group_name}'...\n"\
103
- " - OK\n"\
104
- " - remove association {group:#{group_name} <-> host:#{host_name}}...\n"\
105
- " - OK\n"\
106
- " - add automatic association {group:ungrouped <-> host:#{host_name}}...\n"\
107
- " - OK\n"\
108
- " - all OK\n"\
84
+ "Dissociate group '#{group_name}' from host(s) '#{host_name}':\n " \
85
+ "- retrieve group '#{group_name}'...\n " \
86
+ "- OK\n " \
87
+ "- remove association {group:#{group_name} <-> host:#{host_name}}...\n " \
88
+ "- OK\n " \
89
+ "- add automatic association {group:ungrouped <-> host:#{host_name}}...\n " \
90
+ "- OK\n " \
91
+ "- all OK\n" \
109
92
  "Succeeded.\n"
110
93
  expected(actual, desired)
111
- # rubocop:enable Metrics/LineLength
112
-
113
94
  # We should have the correct group associations
114
95
  group = @db.models[:group].find(name: group_name)
115
96
  hosts = group.hosts_dataset
116
97
  expect(hosts.count).to eq(0)
117
98
  end
118
99
 
100
+ #------------------------
101
+ it 'group rmhost GROUP HOST --dry-run should not remove membership or add ungrouped' do
102
+ host_name = 'test1'
103
+ group_name = 'group1'
104
+ runner { @app.start(%W[host add #{host_name}]) }
105
+ runner { @app.start(%W[group add #{group_name}]) }
106
+ runner { @app.start(%W[group addhost #{group_name} #{host_name}]) }
107
+
108
+ actual = runner { @app.start(%W[group rmhost #{group_name} #{host_name} --dry-run]) }
109
+
110
+ expect(actual[:unexpected]).to eq(false)
111
+ expect(actual[:aborted]).to eq(false)
112
+ expect(actual[:STDOUT]).to include('Dry run complete. No changes applied.')
113
+ group = @db.models[:group].find(name: group_name)
114
+ host = @db.models[:host].find(name: host_name)
115
+ expect(group.hosts_dataset[name: host_name]).not_to be_nil
116
+ expect(host.groups_dataset[name: 'ungrouped']).to be_nil
117
+ end
118
+
119
119
  #------------------------
120
120
  it 'GROUP HOST ... should warn about non-existing associations' do
121
121
  # 1. Should warn that the association doesn't exist.
@@ -123,28 +123,27 @@ RSpec.describe Moose::Inventory::Cli::Group do
123
123
 
124
124
  host_name = 'test_host'
125
125
  group_name = 'test_group'
126
- runner { @app.start(%W(host add #{host_name})) }
127
- runner { @app.start(%W(group add #{group_name})) }
128
- runner { @app.start(%W(group addhost #{host_name})) }
126
+ runner { @app.start(%W[host add #{host_name}]) }
127
+ runner { @app.start(%W[group add #{group_name}]) }
128
+ runner { @app.start(%W[group addhost #{host_name}]) }
129
129
 
130
130
  actual = runner do
131
- @app.start(%W(group rmhost #{group_name} #{host_name}))
131
+ @app.start(%W[group rmhost #{group_name} #{host_name} --yes])
132
132
  end
133
133
 
134
- # rubocop:disable Metrics/LineLength
135
134
  desired = { aborted: false }
136
135
  desired[:STDOUT] =
137
- "Dissociate group '#{group_name}' from host(s) '#{host_name}':\n"\
138
- " - retrieve group \'#{group_name}\'...\n"\
139
- " - OK\n"\
140
- " - remove association {group:#{group_name} <-> host:#{host_name}}...\n"\
141
- " - doesn't exist, skipping.\n"\
142
- " - OK\n"\
143
- " - all OK\n"\
136
+ "Dissociate group '#{group_name}' from host(s) '#{host_name}':\n " \
137
+ "- retrieve group '#{group_name}'...\n " \
138
+ "- OK\n " \
139
+ "- remove association {group:#{group_name} <-> host:#{host_name}}...\n " \
140
+ "- doesn't exist, skipping.\n " \
141
+ "- OK\n " \
142
+ "- all OK\n" \
144
143
  "Succeeded, with warnings.\n"
145
144
  desired[:STDERR] =
146
- "WARNING: Association {group:#{group_name} <-> host:#{host_name}} "\
147
- "doesn't exist, skipping.\n"
145
+ "WARNING: Association {group:#{group_name} <-> host:#{host_name}} " \
146
+ "doesn't exist, skipping.\n"
148
147
 
149
148
  expected(actual, desired)
150
149
  end
@@ -154,9 +153,9 @@ RSpec.describe Moose::Inventory::Cli::Group do
154
153
  host_name = 'test_host'
155
154
  group_name = 'ungrouped'
156
155
 
157
- runner { @app.start(%W(host add #{name})) } # <- auto creates the association with ungrouped
156
+ runner { @app.start(%W[host add #{name}]) } # <- auto creates the association with ungrouped
158
157
 
159
- actual = runner { @app.start(%W(group rmhost #{group_name} #{host_name})) }
158
+ actual = runner { @app.start(%W[group rmhost #{group_name} #{host_name} --yes]) }
160
159
 
161
160
  desired = { aborted: true }
162
161
  desired[:STDERR] =
@@ -165,41 +164,39 @@ RSpec.describe Moose::Inventory::Cli::Group do
165
164
  end
166
165
 
167
166
  #------------------------
168
- it 'GROUP HOST1 HOST2 ... should dissociate the group from'\
169
- ' multiple hosts at once' do
167
+ it 'GROUP HOST1 HOST2 ... should dissociate the group from ' \
168
+ 'multiple hosts at once' do
170
169
  # 1. Should dissociate hosts from the group
171
170
  # 2. Should add each host to the 'ungrouped' automatic group
172
171
  # if it has no other groups.
173
172
 
174
173
  group_name = 'test_group'
175
- host_names = %w(test_host1 test_host2 test_host3)
174
+ host_names = %w[test_host1 test_host2 test_host3]
176
175
 
177
- runner { @app.start(%W(group add #{group_name})) }
178
- runner { @app.start(%W(group addhost #{group_name}) + host_names) }
176
+ runner { @app.start(%W[group add #{group_name}]) }
177
+ runner { @app.start(%W[group addhost #{group_name}] + host_names) }
179
178
 
180
179
  actual = runner do
181
- @app.start(%W(group rmhost #{group_name}) + host_names)
180
+ @app.start(%W[group rmhost #{group_name} --yes] + host_names)
182
181
  end
183
182
 
184
183
  # @console.out(actual, 'y')
185
184
  desired = { aborted: false }
186
185
  desired[:STDOUT] =
187
- "Dissociate group '#{group_name}' from host(s) '#{host_names.join(',')}':\n"\
188
- " - retrieve group \'#{group_name}\'...\n"\
189
- " - OK\n"
186
+ "Dissociate group '#{group_name}' from host(s) '#{host_names.join(',')}':\n " \
187
+ "- retrieve group '#{group_name}'...\n " \
188
+ "- OK\n"
190
189
  host_names.each do |host|
191
190
  desired[:STDOUT] = desired[:STDOUT] +
192
- " - remove association {group:#{group_name} <-> host:#{host}}...\n"\
193
- " - OK\n"\
194
- " - add automatic association {group:ungrouped <-> host:#{host}}...\n"\
195
- " - OK\n"\
191
+ " - remove association {group:#{group_name} <-> host:#{host}}...\n " \
192
+ "- OK\n " \
193
+ "- add automatic association {group:ungrouped <-> host:#{host}}...\n " \
194
+ "- OK\n" \
196
195
  end
197
196
  desired[:STDOUT] = desired[:STDOUT] +
198
- " - all OK\n"\
197
+ " - all OK\n" \
199
198
  "Succeeded.\n"
200
199
  expected(actual, desired)
201
- # rubocop:enable Metrics/LineLength
202
-
203
200
  # We should have the correct group associations
204
201
  group = @db.models[:group].find(name: group_name)
205
202
  hosts = group.hosts_dataset
@@ -210,3 +207,4 @@ RSpec.describe Moose::Inventory::Cli::Group do
210
207
  end
211
208
  end
212
209
  end
210
+ # rubocop:enable Metrics/BlockLength