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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +15 -1
- data/.github/workflows/release.yml +60 -0
- data/.gitignore +2 -1
- data/.gitleaks.toml +9 -0
- data/.rubocop.yml +49 -0
- data/BACKLOG.md +752 -24
- data/Gemfile +2 -0
- data/Gemfile.lock +36 -1
- data/README.md +340 -44
- data/Rakefile +2 -0
- data/bin/moose-inventory +2 -1
- data/docs/architecture/architecture-and-trust-boundaries.md +444 -0
- data/docs/compatibility/cli-output-compatibility.md +76 -0
- data/docs/governance/approval-register.md +37 -0
- data/docs/maintenance/database-backup-restore-guidance.md +162 -0
- data/docs/maintenance/package-maintenance-and-agent-boundaries.md +260 -0
- data/docs/process/conformance-gap-analysis-2026-05-28.md +192 -0
- data/docs/product/product-brief.md +161 -0
- data/docs/product/requirements-baseline.md +477 -0
- data/docs/qa/qa-documentation-and-release-gates.md +283 -0
- data/docs/release/package-provenance-hardening.md +126 -0
- data/docs/release/publishing.md +54 -50
- data/docs/release/release-environment-protection.md +70 -0
- data/docs/release/release-readiness.md +37 -4
- data/docs/security/accepted-risk-register.md +84 -0
- data/docs/security/security-privacy-process.md +287 -0
- data/docs/security-audit-2026-05-26-rerun.md +75 -0
- data/docs/security-audit-2026-05-26.md +63 -0
- data/docs/ux/cli-workflow-notes.md +287 -0
- data/examples/ansible/ansible.cfg +3 -0
- data/examples/ansible/inventory/moose_inventory.yml +5 -0
- data/examples/ansible/inventory_plugins/moose_inventory.py +100 -0
- data/examples/ci/README.md +16 -0
- data/examples/ci/github-actions/inventory-review.yml +38 -0
- data/examples/ci/inventory/example-snapshot.yml +19 -0
- data/examples/ci/scripts/validate-inventory-snapshot.sh +30 -0
- data/lib/moose_inventory/cli/application.rb +133 -5
- data/lib/moose_inventory/cli/association_rendering.rb +74 -0
- data/lib/moose_inventory/cli/association_rendering_support.rb +89 -0
- data/lib/moose_inventory/cli/audit.rb +62 -0
- data/lib/moose_inventory/cli/audit_recording.rb +40 -0
- data/lib/moose_inventory/cli/child_relation_rendering.rb +110 -0
- data/lib/moose_inventory/cli/console.rb +135 -0
- data/lib/moose_inventory/cli/db.rb +64 -0
- data/lib/moose_inventory/cli/factory.rb +28 -0
- data/lib/moose_inventory/cli/formatter.rb +8 -12
- data/lib/moose_inventory/cli/group.rb +7 -1
- data/lib/moose_inventory/cli/group_add.rb +91 -73
- data/lib/moose_inventory/cli/group_addchild.rb +41 -66
- data/lib/moose_inventory/cli/group_addhost.rb +33 -71
- data/lib/moose_inventory/cli/group_addvar.rb +27 -47
- data/lib/moose_inventory/cli/group_get.rb +8 -42
- data/lib/moose_inventory/cli/group_list.rb +7 -40
- data/lib/moose_inventory/cli/group_listvars.rb +9 -55
- data/lib/moose_inventory/cli/group_rm.rb +105 -73
- data/lib/moose_inventory/cli/group_rmchild.rb +47 -57
- data/lib/moose_inventory/cli/group_rmhost.rb +34 -61
- data/lib/moose_inventory/cli/group_rmvar.rb +30 -41
- data/lib/moose_inventory/cli/group_tags.rb +33 -0
- data/lib/moose_inventory/cli/helpers.rb +143 -0
- data/lib/moose_inventory/cli/host.rb +8 -2
- data/lib/moose_inventory/cli/host_add.rb +91 -66
- data/lib/moose_inventory/cli/host_addgroup.rb +39 -66
- data/lib/moose_inventory/cli/host_addvar.rb +28 -52
- data/lib/moose_inventory/cli/host_get.rb +9 -37
- data/lib/moose_inventory/cli/host_list.rb +24 -21
- data/lib/moose_inventory/cli/host_listvars.rb +9 -62
- data/lib/moose_inventory/cli/host_rm.rb +60 -42
- data/lib/moose_inventory/cli/host_rmgroup.rb +39 -55
- data/lib/moose_inventory/cli/host_rmvar.rb +31 -45
- data/lib/moose_inventory/cli/host_tags.rb +33 -0
- data/lib/moose_inventory/cli/listvars_support.rb +55 -0
- data/lib/moose_inventory/cli/plan_rendering.rb +50 -0
- data/lib/moose_inventory/cli/relation_transaction_support.rb +51 -0
- data/lib/moose_inventory/cli/tag_support.rb +97 -0
- data/lib/moose_inventory/cli/variable_rendering.rb +67 -0
- data/lib/moose_inventory/config/config.rb +185 -108
- data/lib/moose_inventory/db/db.rb +188 -193
- data/lib/moose_inventory/db/exceptions.rb +6 -3
- data/lib/moose_inventory/db/models.rb +16 -0
- data/lib/moose_inventory/db/schema_migrations.rb +248 -0
- data/lib/moose_inventory/inventory_context.rb +116 -0
- data/lib/moose_inventory/operations/add_associations.rb +131 -0
- data/lib/moose_inventory/operations/add_groups.rb +123 -0
- data/lib/moose_inventory/operations/add_hosts.rb +123 -0
- data/lib/moose_inventory/operations/add_variables.rb +77 -0
- data/lib/moose_inventory/operations/entity_variable_operation_support.rb +46 -0
- data/lib/moose_inventory/operations/group_child_relations.rb +125 -0
- data/lib/moose_inventory/operations/group_cleanup.rb +70 -0
- data/lib/moose_inventory/operations/import_inventory_snapshot.rb +41 -0
- data/lib/moose_inventory/operations/inventory_doctor.rb +172 -0
- data/lib/moose_inventory/operations/inventory_snapshot.rb +60 -0
- data/lib/moose_inventory/operations/inventory_snapshot_applier.rb +112 -0
- data/lib/moose_inventory/operations/inventory_snapshot_preview.rb +174 -0
- data/lib/moose_inventory/operations/inventory_snapshot_validator.rb +134 -0
- data/lib/moose_inventory/operations/operation_event_support.rb +27 -0
- data/lib/moose_inventory/operations/query_inventory/base_query.rb +24 -0
- data/lib/moose_inventory/operations/query_inventory/group_queries.rb +86 -0
- data/lib/moose_inventory/operations/query_inventory/host_queries.rb +106 -0
- data/lib/moose_inventory/operations/query_inventory.rb +47 -0
- data/lib/moose_inventory/operations/remove_associations.rb +113 -0
- data/lib/moose_inventory/operations/remove_groups.rb +79 -0
- data/lib/moose_inventory/operations/remove_hosts.rb +68 -0
- data/lib/moose_inventory/operations/remove_variables.rb +67 -0
- data/lib/moose_inventory/runtime_options.rb +31 -0
- data/lib/moose_inventory/version.rb +3 -1
- data/lib/moose_inventory.rb +10 -7
- data/moose-inventory.gemspec +22 -35
- data/scripts/check.sh +3 -0
- data/scripts/ci/check_generated_artifacts.sh +41 -0
- data/scripts/ci/check_permissions.sh +5 -0
- data/scripts/ci/check_rubocop.sh +33 -0
- data/scripts/ci/check_secrets.sh +26 -0
- data/scripts/ci/check_security.sh +18 -0
- data/scripts/ci/install_security_tools.sh +47 -0
- data/scripts/files.rb +5 -4
- data/scripts/install_dependencies.sh +2 -0
- data/spec/examples/ci_examples_spec.rb +37 -0
- data/spec/lib/moose_inventory/ansible_plugin_examples_spec.rb +29 -0
- data/spec/lib/moose_inventory/cli/application_doctor_spec.rb +50 -0
- data/spec/lib/moose_inventory/cli/application_import_export_spec.rb +100 -0
- data/spec/lib/moose_inventory/cli/application_spec.rb +25 -15
- data/spec/lib/moose_inventory/cli/audit_spec.rb +56 -0
- data/spec/lib/moose_inventory/cli/cli_spec.rb +15 -19
- data/spec/lib/moose_inventory/cli/console_spec.rb +98 -0
- data/spec/lib/moose_inventory/cli/factory_spec.rb +27 -0
- data/spec/lib/moose_inventory/cli/formatter_spec.rb +95 -3
- data/spec/lib/moose_inventory/cli/group_add_spec.rb +140 -116
- data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +89 -35
- data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +81 -84
- data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +65 -68
- data/spec/lib/moose_inventory/cli/group_get_spec.rb +17 -33
- data/spec/lib/moose_inventory/cli/group_list_spec.rb +16 -38
- data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +33 -40
- data/spec/lib/moose_inventory/cli/group_rm_spec.rb +165 -85
- data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +100 -30
- data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +76 -78
- data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +57 -63
- data/spec/lib/moose_inventory/cli/group_spec.rb +2 -0
- data/spec/lib/moose_inventory/cli/helpers_spec.rb +146 -0
- data/spec/lib/moose_inventory/cli/host_add_spec.rb +170 -116
- data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +100 -83
- data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +92 -74
- data/spec/lib/moose_inventory/cli/host_get_spec.rb +14 -33
- data/spec/lib/moose_inventory/cli/host_list_spec.rb +41 -33
- data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +45 -53
- data/spec/lib/moose_inventory/cli/host_rm_spec.rb +66 -48
- data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +73 -83
- data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +56 -63
- data/spec/lib/moose_inventory/cli/host_spec.rb +2 -0
- data/spec/lib/moose_inventory/cli/tags_spec.rb +81 -0
- data/spec/lib/moose_inventory/config/config_spec.rb +41 -3
- data/spec/lib/moose_inventory/db/db_spec.rb +551 -29
- data/spec/lib/moose_inventory/db/exceptions_spec.rb +18 -0
- data/spec/lib/moose_inventory/db/models_spec.rb +7 -3
- data/spec/lib/moose_inventory/db_lifecycle_spec.rb +73 -0
- data/spec/lib/moose_inventory/inventory_context_spec.rb +10 -0
- data/spec/lib/moose_inventory/operations/add_associations_spec.rb +111 -0
- data/spec/lib/moose_inventory/operations/add_groups_spec.rb +80 -0
- data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +82 -0
- data/spec/lib/moose_inventory/operations/add_variables_spec.rb +103 -0
- data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +122 -0
- data/spec/lib/moose_inventory/operations/import_inventory_snapshot_spec.rb +226 -0
- data/spec/lib/moose_inventory/operations/inventory_doctor_spec.rb +77 -0
- data/spec/lib/moose_inventory/operations/inventory_snapshot_spec.rb +50 -0
- data/spec/lib/moose_inventory/operations/operation_event_support_spec.rb +78 -0
- data/spec/lib/moose_inventory/operations/query_inventory_spec.rb +146 -0
- data/spec/lib/moose_inventory/operations/remove_associations_spec.rb +113 -0
- data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +78 -0
- data/spec/lib/moose_inventory/operations/remove_hosts_spec.rb +55 -0
- data/spec/lib/moose_inventory/operations/remove_variables_spec.rb +83 -0
- data/spec/shared/shared_config_setup.rb +4 -3
- data/spec/spec_helper.rb +50 -40
- data/spec/support/cli_harness.rb +33 -0
- metadata +163 -35
|
@@ -1,51 +1,34 @@
|
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
@mockargs = []
|
|
16
|
-
@mockarg_parts.each do |key, val|
|
|
17
|
-
@mockargs << "--#{key}"
|
|
18
|
-
@mockargs << val
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
@config = Moose::Inventory::Config
|
|
22
|
-
@config.init(@mockargs)
|
|
23
|
-
|
|
24
|
-
@console = Moose::Inventory::Cli::Formatter
|
|
25
|
-
|
|
26
|
-
@db = Moose::Inventory::DB
|
|
27
|
-
@db.init if @db.db.nil?
|
|
28
|
-
|
|
29
|
-
@group = Moose::Inventory::Cli::Group
|
|
30
|
-
@host = Moose::Inventory::Cli::Host
|
|
31
|
-
@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
|
+
)
|
|
32
15
|
end
|
|
33
16
|
|
|
34
17
|
before(:each) do
|
|
35
|
-
|
|
18
|
+
reset_cli_harness
|
|
36
19
|
end
|
|
37
20
|
|
|
38
21
|
#======================
|
|
39
22
|
describe 'rm' do
|
|
40
23
|
#---------------
|
|
41
24
|
it 'Group.rm() should be responsive' do
|
|
42
|
-
result = @group.
|
|
25
|
+
result = @group.method_defined?(:rm, false)
|
|
43
26
|
expect(result).to eq(true)
|
|
44
27
|
end
|
|
45
28
|
|
|
46
29
|
#---------------
|
|
47
30
|
it '<missing argument> ... should abort with an error' do
|
|
48
|
-
actual = runner { @app.start(%w
|
|
31
|
+
actual = runner { @app.start(%w[host rm]) }
|
|
49
32
|
|
|
50
33
|
# Check output
|
|
51
34
|
desired = { aborted: true, STDERR: '', STDOUT: '' }
|
|
@@ -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
|
|
41
|
+
actual = runner { @app.start(%w[group rm ungrouped]) }
|
|
59
42
|
|
|
60
43
|
# Check output
|
|
61
44
|
desired = { aborted: true }
|
|
@@ -73,16 +56,16 @@ RSpec.describe Moose::Inventory::Cli::Group do
|
|
|
73
56
|
|
|
74
57
|
# no items in the db
|
|
75
58
|
group_name = 'fake'
|
|
76
|
-
actual = runner { @app.start(%W
|
|
59
|
+
actual = runner { @app.start(%W[group rm #{group_name} --yes]) }
|
|
77
60
|
|
|
78
61
|
# @console.out(actual,'y')
|
|
79
62
|
desired = {}
|
|
80
63
|
desired[:STDOUT] =
|
|
81
|
-
"Remove group '#{group_name}':\n"\
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
64
|
+
"Remove group '#{group_name}':\n " \
|
|
65
|
+
"- Retrieve group '#{group_name}'...\n " \
|
|
66
|
+
"- No such group, skipping.\n " \
|
|
67
|
+
"- OK\n " \
|
|
68
|
+
"- All OK\n" \
|
|
86
69
|
"Succeeded, with warnings.\n"
|
|
87
70
|
desired[:STDERR] =
|
|
88
71
|
"WARNING: Group '#{group_name}' does not exist, skipping.\n"
|
|
@@ -95,17 +78,17 @@ RSpec.describe Moose::Inventory::Cli::Group do
|
|
|
95
78
|
group_name = 'test1'
|
|
96
79
|
@db.models[:group].create(name: group_name)
|
|
97
80
|
|
|
98
|
-
actual = runner { @app.start(%W
|
|
81
|
+
actual = runner { @app.start(%W[group rm #{group_name} --yes]) }
|
|
99
82
|
|
|
100
83
|
# Check output
|
|
101
84
|
desired = {}
|
|
102
85
|
desired[:STDOUT] =
|
|
103
|
-
"Remove group '#{group_name}':\n"\
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
86
|
+
"Remove group '#{group_name}':\n " \
|
|
87
|
+
"- Retrieve group '#{group_name}'...\n " \
|
|
88
|
+
"- OK\n " \
|
|
89
|
+
"- Destroy group '#{group_name}'...\n " \
|
|
90
|
+
"- OK\n " \
|
|
91
|
+
"- All OK\n" \
|
|
109
92
|
"Succeeded.\n"
|
|
110
93
|
expected(actual, desired)
|
|
111
94
|
|
|
@@ -114,12 +97,68 @@ RSpec.describe Moose::Inventory::Cli::Group do
|
|
|
114
97
|
expect(group).to be_nil
|
|
115
98
|
end
|
|
116
99
|
|
|
100
|
+
#---------------
|
|
101
|
+
it 'GROUP without --yes or --dry-run should abort before deleting' do
|
|
102
|
+
group_name = 'group1'
|
|
103
|
+
@db.models[:group].create(name: group_name)
|
|
104
|
+
|
|
105
|
+
actual = runner { @app.start(%W[group rm #{group_name}]) }
|
|
106
|
+
|
|
107
|
+
desired = {
|
|
108
|
+
aborted: true,
|
|
109
|
+
STDOUT: '',
|
|
110
|
+
STDERR: "ERROR: group rm #{group_name} is destructive. Re-run with --yes to confirm, " \
|
|
111
|
+
"or use --dry-run to preview.\n"
|
|
112
|
+
}
|
|
113
|
+
expected(actual, desired)
|
|
114
|
+
expect(@db.models[:group].find(name: group_name)).not_to be_nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
#---------------
|
|
118
|
+
it 'GROUP --dry-run should show planned removal without deleting the group' do
|
|
119
|
+
group_name = 'test1'
|
|
120
|
+
@db.models[:group].create(name: group_name)
|
|
121
|
+
|
|
122
|
+
actual = runner { @app.start(%W[group rm #{group_name} --dry-run]) }
|
|
123
|
+
|
|
124
|
+
desired = {}
|
|
125
|
+
desired[:STDOUT] =
|
|
126
|
+
"Remove group '#{group_name}':\n " \
|
|
127
|
+
"- Retrieve group '#{group_name}'...\n " \
|
|
128
|
+
"- OK\n " \
|
|
129
|
+
"- Destroy group '#{group_name}'...\n " \
|
|
130
|
+
"- OK\n " \
|
|
131
|
+
"- All OK\n" \
|
|
132
|
+
"Dry run complete. No changes applied.\n" \
|
|
133
|
+
"Succeeded.\n"
|
|
134
|
+
|
|
135
|
+
expected(actual, desired)
|
|
136
|
+
expect(@db.models[:group].find(name: group_name)).not_to be_nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
#---------------
|
|
140
|
+
it 'GROUP --recursive --dry-run should show recursive cleanup without deleting groups' do
|
|
141
|
+
runner { @app.start(%w[group add parent]) }
|
|
142
|
+
runner { @app.start(%w[group add child --hosts child-host]) }
|
|
143
|
+
runner { @app.start(%w[group addchild parent child]) }
|
|
144
|
+
|
|
145
|
+
actual = runner { @app.start(%w[group rm --recursive parent --dry-run]) }
|
|
146
|
+
|
|
147
|
+
expect(actual[:unexpected]).to eq(false)
|
|
148
|
+
expect(actual[:aborted]).to eq(false)
|
|
149
|
+
expect(actual[:STDOUT]).to include("- Recursively delete orphaned group 'child'...\n")
|
|
150
|
+
expect(actual[:STDOUT]).to include('Dry run complete. No changes applied.')
|
|
151
|
+
expect(@db.models[:group].find(name: 'parent')).not_to be_nil
|
|
152
|
+
expect(@db.models[:group].find(name: 'child')).not_to be_nil
|
|
153
|
+
host = @db.models[:host].find(name: 'child-host')
|
|
154
|
+
expect(host.groups_dataset[name: 'ungrouped']).to be_nil
|
|
155
|
+
end
|
|
117
156
|
#---------------
|
|
118
157
|
it "GROUP ... should handle the automatic 'ungrouped' group for associated hosts" do
|
|
119
158
|
host_name = 'test-host1'
|
|
120
159
|
group_name = 'test-group1'
|
|
121
160
|
|
|
122
|
-
tmp = runner { @app.start(%W
|
|
161
|
+
tmp = runner { @app.start(%W[group add #{group_name} --hosts #{host_name}]) }
|
|
123
162
|
expect(tmp[:unexpected]).to eq(false)
|
|
124
163
|
expect(tmp[:aborted]).to eq(false)
|
|
125
164
|
host = @db.models[:host].find(name: host_name)
|
|
@@ -128,21 +167,21 @@ RSpec.describe Moose::Inventory::Cli::Group do
|
|
|
128
167
|
expect(groups_ds[name: 'ungrouped']).to be_nil # Shouldn't be ungrouped
|
|
129
168
|
|
|
130
169
|
# Now do the rm
|
|
131
|
-
actual = runner { @app.start(%W
|
|
170
|
+
actual = runner { @app.start(%W[group rm #{group_name} --yes]) }
|
|
132
171
|
|
|
133
172
|
# @console.out(actual)
|
|
134
173
|
|
|
135
174
|
# Check output
|
|
136
175
|
desired = {}
|
|
137
176
|
desired[:STDOUT] =
|
|
138
|
-
"Remove group '#{group_name}':\n"\
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
"
|
|
177
|
+
"Remove group '#{group_name}':\n " \
|
|
178
|
+
"- Retrieve group '#{group_name}'...\n " \
|
|
179
|
+
"- OK\n " \
|
|
180
|
+
"- Adding automatic association {group:ungrouped <-> host:#{host_name}}...\n " \
|
|
181
|
+
"- OK\n " \
|
|
182
|
+
"- Destroy group '#{group_name}'...\n " \
|
|
183
|
+
"- OK\n " \
|
|
184
|
+
"- All OK\n" \
|
|
146
185
|
"Succeeded.\n"
|
|
147
186
|
expected(actual, desired)
|
|
148
187
|
|
|
@@ -159,26 +198,26 @@ RSpec.describe Moose::Inventory::Cli::Group do
|
|
|
159
198
|
|
|
160
199
|
#---------------
|
|
161
200
|
it 'GROUP1 GROUP2 ... should remove multiple groups' do
|
|
162
|
-
names = %w
|
|
201
|
+
names = %w[group1 group2 group3]
|
|
163
202
|
names.each do |name|
|
|
164
203
|
@db.models[:group].create(name: name)
|
|
165
204
|
end
|
|
166
205
|
|
|
167
|
-
actual = runner { @app.start(%w
|
|
206
|
+
actual = runner { @app.start(%w[group rm --yes] + names) }
|
|
168
207
|
|
|
169
208
|
# Check output
|
|
170
209
|
desired = { STDOUT: '' }
|
|
171
210
|
names.each do |name|
|
|
172
211
|
# Check output
|
|
173
212
|
desired[:STDOUT] = desired[:STDOUT] +
|
|
174
|
-
"Remove group '#{name}':\n"\
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"
|
|
213
|
+
"Remove group '#{name}':\n " \
|
|
214
|
+
"- Retrieve group '#{name}'...\n " \
|
|
215
|
+
"- OK\n " \
|
|
216
|
+
"- Destroy group '#{name}'...\n " \
|
|
217
|
+
"- OK\n " \
|
|
218
|
+
"- All OK\n"
|
|
180
219
|
end
|
|
181
|
-
desired[:STDOUT] = desired[:STDOUT]
|
|
220
|
+
desired[:STDOUT] = "#{desired[:STDOUT]}Succeeded.\n"
|
|
182
221
|
expected(actual, desired)
|
|
183
222
|
|
|
184
223
|
# Check db
|
|
@@ -189,24 +228,24 @@ RSpec.describe Moose::Inventory::Cli::Group do
|
|
|
189
228
|
#---------------
|
|
190
229
|
it 'GROUP ... should remove GROUP, where GROUP has an associated parent.' do
|
|
191
230
|
@db.models[:group].create(name: 'parent')
|
|
192
|
-
runner { @app.start(%w
|
|
231
|
+
runner { @app.start(%w[group addchild parent child]) }
|
|
193
232
|
|
|
194
|
-
actual = runner { @app.start(%w
|
|
233
|
+
actual = runner { @app.start(%w[group rm child --yes]) }
|
|
195
234
|
|
|
196
235
|
# Check output
|
|
197
236
|
desired = { STDOUT: '' }
|
|
198
237
|
# Check output
|
|
199
238
|
desired[:STDOUT] = desired[:STDOUT] +
|
|
200
|
-
"Remove group 'child':\n"\
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
|
|
209
|
-
desired[:STDOUT] = desired[:STDOUT]
|
|
239
|
+
"Remove group 'child':\n " \
|
|
240
|
+
"- Retrieve group 'child'...\n " \
|
|
241
|
+
"- OK\n " \
|
|
242
|
+
"- Remove association {group:child <-> group:parent}...\n " \
|
|
243
|
+
"- OK\n " \
|
|
244
|
+
"- Destroy group 'child'...\n " \
|
|
245
|
+
"- OK\n " \
|
|
246
|
+
"- All OK\n"
|
|
247
|
+
|
|
248
|
+
desired[:STDOUT] = "#{desired[:STDOUT]}Succeeded.\n"
|
|
210
249
|
expected(actual, desired)
|
|
211
250
|
|
|
212
251
|
# Check db
|
|
@@ -217,29 +256,70 @@ RSpec.describe Moose::Inventory::Cli::Group do
|
|
|
217
256
|
#---------------
|
|
218
257
|
it 'GROUP ... should remove GROUP, where GROUP has an associated child.' do
|
|
219
258
|
@db.models[:group].create(name: 'parent')
|
|
220
|
-
runner { @app.start(%w
|
|
259
|
+
runner { @app.start(%w[group addchild parent child]) }
|
|
221
260
|
|
|
222
|
-
actual = runner { @app.start(%w
|
|
261
|
+
actual = runner { @app.start(%w[group rm parent --yes]) }
|
|
223
262
|
|
|
224
263
|
# Check output
|
|
225
264
|
desired = { STDOUT: '' }
|
|
226
265
|
# Check output
|
|
227
266
|
desired[:STDOUT] = desired[:STDOUT] +
|
|
228
|
-
"Remove group 'parent':\n"\
|
|
229
|
-
"
|
|
230
|
-
"
|
|
231
|
-
"
|
|
232
|
-
"
|
|
233
|
-
"
|
|
234
|
-
"
|
|
235
|
-
"
|
|
236
|
-
|
|
237
|
-
desired[:STDOUT] = desired[:STDOUT]
|
|
267
|
+
"Remove group 'parent':\n " \
|
|
268
|
+
"- Retrieve group 'parent'...\n " \
|
|
269
|
+
"- OK\n " \
|
|
270
|
+
"- Remove association {group:parent <-> group:child}...\n " \
|
|
271
|
+
"- OK\n " \
|
|
272
|
+
"- Destroy group 'parent'...\n " \
|
|
273
|
+
"- OK\n " \
|
|
274
|
+
"- All OK\n"
|
|
275
|
+
|
|
276
|
+
desired[:STDOUT] = "#{desired[:STDOUT]}Succeeded.\n"
|
|
238
277
|
expected(actual, desired)
|
|
239
278
|
|
|
240
279
|
# Check db
|
|
241
280
|
groups = @db.models[:group].all
|
|
242
281
|
expect(groups.count).to eq(1)
|
|
243
282
|
end
|
|
283
|
+
|
|
284
|
+
#---------------
|
|
285
|
+
it 'GROUP --recursive ... should remove orphaned child groups recursively' do
|
|
286
|
+
runner { @app.start(%w[group add parent]) }
|
|
287
|
+
runner { @app.start(%w[group add child --hosts child-host]) }
|
|
288
|
+
runner { @app.start(%w[group add grandchild]) }
|
|
289
|
+
runner { @app.start(%w[group addchild parent child]) }
|
|
290
|
+
runner { @app.start(%w[group addchild child grandchild]) }
|
|
291
|
+
|
|
292
|
+
actual = runner { @app.start(%w[group rm --recursive parent --yes]) }
|
|
293
|
+
|
|
294
|
+
expect(actual[:unexpected]).to eq(false)
|
|
295
|
+
expect(actual[:aborted]).to eq(false)
|
|
296
|
+
expect(actual[:STDOUT]).to include("- Recursively delete orphaned group 'child'...\n")
|
|
297
|
+
expect(actual[:STDOUT]).to include("- Recursively delete orphaned group 'grandchild'...\n")
|
|
298
|
+
|
|
299
|
+
%w[parent child grandchild].each do |name|
|
|
300
|
+
expect(@db.models[:group].find(name: name)).to be_nil
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
host = @db.models[:host].find(name: 'child-host')
|
|
304
|
+
expect(host.groups_dataset[name: 'ungrouped']).not_to be_nil
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
#---------------
|
|
308
|
+
it 'GROUP --recursive ... should not remove child groups with another parent' do
|
|
309
|
+
runner { @app.start(%w[group add parent other-parent]) }
|
|
310
|
+
runner { @app.start(%w[group addchild parent child]) }
|
|
311
|
+
runner { @app.start(%w[group addchild other-parent child]) }
|
|
312
|
+
|
|
313
|
+
actual = runner { @app.start(%w[group rm --recursive parent --yes]) }
|
|
314
|
+
|
|
315
|
+
expect(actual[:unexpected]).to eq(false)
|
|
316
|
+
expect(actual[:aborted]).to eq(false)
|
|
317
|
+
expect(@db.models[:group].find(name: 'parent')).to be_nil
|
|
318
|
+
|
|
319
|
+
child = @db.models[:group].find(name: 'child')
|
|
320
|
+
expect(child).not_to be_nil
|
|
321
|
+
expect(child.parents_dataset[name: 'other-parent']).not_to be_nil
|
|
322
|
+
end
|
|
244
323
|
end
|
|
245
324
|
end
|
|
325
|
+
# rubocop:enable Metrics/BlockLength
|
|
@@ -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:
|
|
11
|
-
format:
|
|
12
|
-
env:
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
"
|
|
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
|
|
121
|
+
runner { @app.start(%W[group add #{pname} #{cname}]) }
|
|
120
122
|
|
|
121
123
|
actual = runner do
|
|
122
|
-
@app.start(%W
|
|
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
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
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
|
|
151
|
-
runner { @app.start(%W
|
|
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
|
|
156
|
+
@app.start(%W[group rmchild #{pname} #{cname} --yes])
|
|
155
157
|
end
|
|
156
158
|
|
|
157
159
|
# @console.out(actual, 'y')
|
|
@@ -159,15 +161,83 @@ 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
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
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
|
|
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
|
+
|
|
197
|
+
#------------------------
|
|
198
|
+
it 'GROUP CHILDGROUP --delete-orphans ... should delete orphaned child groups recursively' do
|
|
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]) }
|
|
204
|
+
|
|
205
|
+
actual = runner do
|
|
206
|
+
@app.start(%w[group rmchild --delete-orphans parent child --yes])
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
expect(actual[:unexpected]).to eq(false)
|
|
210
|
+
expect(actual[:aborted]).to eq(false)
|
|
211
|
+
expect(actual[:STDOUT]).to include("- Recursively delete orphaned group 'child'...\n")
|
|
212
|
+
expect(actual[:STDOUT]).to include("- Recursively delete orphaned group 'grandchild'...\n")
|
|
213
|
+
|
|
214
|
+
expect(@db.models[:group].find(name: 'parent')).not_to be_nil
|
|
215
|
+
%w[child grandchild].each do |name|
|
|
216
|
+
expect(@db.models[:group].find(name: name)).to be_nil
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
host = @db.models[:host].find(name: 'child-host')
|
|
220
|
+
expect(host.groups_dataset[name: 'ungrouped']).not_to be_nil
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
#------------------------
|
|
224
|
+
it 'GROUP CHILDGROUP --delete-orphans ... should preserve child groups with another parent' do
|
|
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]) }
|
|
228
|
+
|
|
229
|
+
actual = runner do
|
|
230
|
+
@app.start(%w[group rmchild --delete-orphans parent child --yes])
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
expect(actual[:unexpected]).to eq(false)
|
|
234
|
+
expect(actual[:aborted]).to eq(false)
|
|
235
|
+
|
|
236
|
+
child = @db.models[:group].find(name: 'child')
|
|
237
|
+
expect(child).not_to be_nil
|
|
238
|
+
expect(child.parents_dataset[name: 'parent']).to be_nil
|
|
239
|
+
expect(child.parents_dataset[name: 'other-parent']).not_to be_nil
|
|
240
|
+
end
|
|
172
241
|
end
|
|
173
242
|
end
|
|
243
|
+
# rubocop:enable Metrics/BlockLength
|