moose-inventory 2.0 → 2.1.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/release.yml +2 -0
- data/.gitignore +6 -1
- data/.rubocop.yml +21 -0
- data/BACKLOG.md +638 -9
- data/Gemfile +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +315 -39
- 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 +11 -3
- data/docs/release/release-environment-protection.md +78 -0
- data/docs/release/release-readiness.md +23 -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 +2 -2
- data/docs/security-audit-2026-05-29-snapshot-import-fuzz.md +58 -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 +135 -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 +5 -2
- data/lib/moose_inventory/cli/group_add.rb +11 -9
- data/lib/moose_inventory/cli/group_addchild.rb +23 -65
- data/lib/moose_inventory/cli/group_addhost.rb +16 -67
- 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 +12 -10
- data/lib/moose_inventory/cli/group_rmchild.rb +26 -82
- data/lib/moose_inventory/cli/group_rmhost.rb +18 -53
- 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 +68 -1
- data/lib/moose_inventory/cli/host.rb +6 -3
- data/lib/moose_inventory/cli/host_add.rb +69 -29
- data/lib/moose_inventory/cli/host_addgroup.rb +22 -58
- 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 +25 -44
- 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 +170 -195
- 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 +68 -2
- data/lib/moose_inventory/operations/add_associations.rb +20 -16
- data/lib/moose_inventory/operations/add_groups.rb +21 -13
- data/lib/moose_inventory/operations/add_hosts.rb +30 -17
- 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 +23 -16
- data/lib/moose_inventory/operations/group_cleanup.rb +23 -8
- 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 +174 -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 +30 -18
- data/lib/moose_inventory/operations/remove_groups.rb +12 -12
- 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 +19 -35
- data/scripts/check.sh +1 -0
- data/scripts/ci/check_generated_artifacts.sh +41 -0
- data/scripts/ci/check_permissions.sh +2 -0
- data/scripts/ci/check_rubocop.sh +30 -25
- data/scripts/ci/check_security.sh +4 -1
- data/scripts/files.rb +5 -4
- 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 +132 -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 +136 -96
- data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +66 -41
- 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 +396 -36
- 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 +34 -0
- data/spec/lib/moose_inventory/operations/add_groups_spec.rb +15 -0
- data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +13 -0
- data/spec/lib/moose_inventory/operations/add_variables_spec.rb +103 -0
- data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +46 -0
- data/spec/lib/moose_inventory/operations/import_inventory_snapshot_spec.rb +239 -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 +35 -0
- data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +21 -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 +81 -41
|
@@ -19,107 +19,51 @@ module Moose
|
|
|
19
19
|
desc: 'Delete child groups that become orphaned'
|
|
20
20
|
desc 'rmchild PARENTGROUP CHILDGROUP_1 [CHILDGROUP_2 ... ]',
|
|
21
21
|
'Dissociate one or more child-groups CHILDGROUP_n from PARENTGROUP'
|
|
22
|
+
option :dry_run, type: :boolean
|
|
23
|
+
option :yes, type: :boolean, desc: 'Confirm destructive dissociation without prompting'
|
|
24
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
22
25
|
def rmchild(*argv)
|
|
23
26
|
abort_if_missing_args(argv, 2, '2 or more')
|
|
27
|
+
validate_machine_plan_request!
|
|
24
28
|
|
|
25
29
|
pname = argv[0].downcase
|
|
26
30
|
cnames = normalize_names(argv.slice(1, argv.length - 1))
|
|
27
31
|
|
|
28
32
|
abort_if_automatic_group([pname] + cnames)
|
|
33
|
+
confirm_destructive_action!("group rmchild #{pname} #{cnames.join(',')}")
|
|
29
34
|
|
|
30
35
|
result = remove_children_from_group(pname, cnames)
|
|
36
|
+
return if machine_plan_output_rendered?(result, command: 'group rmchild')
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
puts 'Succeeded, with warnings.'
|
|
36
|
-
end
|
|
38
|
+
record_audit({ command: 'group rmchild', action: 'dissociate_child', entity_type: 'group',
|
|
39
|
+
entity_names: pname }, result: result, dry_run: options[:dry_run])
|
|
40
|
+
print_warning_summary(result)
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
private
|
|
40
44
|
|
|
41
45
|
def remove_children_from_group(parent_name, child_names)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return result
|
|
58
|
-
end
|
|
59
|
-
rescue db.exceptions[:moose] => e
|
|
60
|
-
abort("ERROR: #{e}")
|
|
46
|
+
operation = build_operation(Moose::Inventory::Operations::GroupChildRelations)
|
|
47
|
+
run_group_relation_transaction(
|
|
48
|
+
heading: "Dissociate parent group '#{parent_name}' from child group(s) '#{child_names.join(',')}':",
|
|
49
|
+
on_error: method(:exception_to_s)
|
|
50
|
+
) do
|
|
51
|
+
parent_group = fetch_existing_group_or_abort(parent_name)
|
|
52
|
+
result = operation.remove_children(
|
|
53
|
+
parent_group: parent_group,
|
|
54
|
+
parent_name: parent_name,
|
|
55
|
+
child_names: child_names,
|
|
56
|
+
delete_orphans: options[:delete_orphans],
|
|
57
|
+
dry_run: options[:dry_run]
|
|
58
|
+
)
|
|
59
|
+
render_rmchild_events(result.events) unless machine_plan_output_requested?
|
|
60
|
+
result
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
def fetch_existing_group_for_rmchild(context, name)
|
|
65
|
-
fmt.puts 2, "- retrieve group '#{name}'..."
|
|
66
|
-
group = context.find_group(name)
|
|
67
|
-
abort("ERROR: The group '#{name}' does not exist.") if group.nil?
|
|
68
|
-
|
|
69
|
-
fmt.puts 4, '- OK'
|
|
70
|
-
group
|
|
71
|
-
end
|
|
72
|
-
|
|
73
64
|
def render_rmchild_events(events)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def render_rmchild_event(event)
|
|
78
|
-
payload = event.payload
|
|
79
|
-
|
|
80
|
-
return render_rmchild_warning(payload) if event.type == :child_association_missing
|
|
81
|
-
return render_rmchild_missing(payload) if event.type == :missing_skipping
|
|
82
|
-
return render_rmchild_progress(event.type, payload) if rmchild_progress_event?(event.type)
|
|
83
|
-
|
|
84
|
-
render_rmchild_status(event.type, payload)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def rmchild_progress_event?(type)
|
|
88
|
-
%i[
|
|
89
|
-
removing_child_association
|
|
90
|
-
recursively_delete_orphaned_group
|
|
91
|
-
removing_recursive_child_association
|
|
92
|
-
].include?(type)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def render_rmchild_progress(type, payload)
|
|
96
|
-
case type
|
|
97
|
-
when :removing_child_association
|
|
98
|
-
fmt.puts 2, "- remove association {group:#{payload[:parent]} <-> group:#{payload[:child]}}..."
|
|
99
|
-
when :recursively_delete_orphaned_group
|
|
100
|
-
fmt.puts 2, "- Recursively delete orphaned group '#{payload[:name]}'..."
|
|
101
|
-
when :removing_recursive_child_association
|
|
102
|
-
fmt.puts 4, "- Remove association {group:#{payload[:parent]} <-> group:#{payload[:child]}}..."
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def render_rmchild_status(type, payload)
|
|
107
|
-
case type
|
|
108
|
-
when :adding_automatic_group_to_host
|
|
109
|
-
fmt.puts payload[:indent], "- Adding automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
110
|
-
when :destroying_group
|
|
111
|
-
fmt.puts payload[:indent], "- Destroy group '#{payload[:name]}'..."
|
|
112
|
-
when :ok
|
|
113
|
-
fmt.puts payload[:indent], '- OK'
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def render_rmchild_warning(payload)
|
|
118
|
-
fmt.warn "Association {group:#{payload[:parent]} <-> group:#{payload[:child]}} does not exist, skipping.\n"
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def render_rmchild_missing(payload)
|
|
122
|
-
fmt.puts payload[:indent], "- doesn't exist, skipping."
|
|
65
|
+
emitter = rmchild_emitter
|
|
66
|
+
events.each { |event| emitter.call(event) }
|
|
123
67
|
end
|
|
124
68
|
end
|
|
125
69
|
end
|
|
@@ -14,78 +14,43 @@ module Moose
|
|
|
14
14
|
#==========================
|
|
15
15
|
desc 'rmhost GROUPNAME HOSTNAME_1 [HOSTNAME_2 ...]',
|
|
16
16
|
'Dissociate the hosts HOSTNAME_n from the group NAME'
|
|
17
|
+
option :dry_run, type: :boolean
|
|
18
|
+
option :yes, type: :boolean, desc: 'Confirm destructive dissociation without prompting'
|
|
19
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
17
20
|
def rmhost(*args)
|
|
18
21
|
abort_if_missing_args(args, 2, '2 or more')
|
|
22
|
+
validate_machine_plan_request!
|
|
19
23
|
|
|
20
24
|
name = args[0].downcase
|
|
21
25
|
hosts = normalize_names(args.slice(1, args.length - 1))
|
|
22
26
|
|
|
23
27
|
abort_if_automatic_group([name])
|
|
28
|
+
confirm_destructive_action!("group rmhost #{name} #{hosts.join(',')}")
|
|
24
29
|
|
|
25
30
|
result = remove_hosts_from_group(name, hosts)
|
|
31
|
+
return if machine_plan_output_rendered?(result, command: 'group rmhost')
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
puts 'Succeeded, with warnings.'
|
|
31
|
-
end
|
|
33
|
+
record_audit({ command: 'group rmhost', action: 'dissociate', entity_type: 'group',
|
|
34
|
+
entity_names: name }, result: result, dry_run: options[:dry_run])
|
|
35
|
+
print_warning_summary(result)
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
private
|
|
35
39
|
|
|
36
40
|
def remove_hosts_from_group(name, hosts)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
result = operation.group_from_hosts(group: group, group_name: name, host_names: hosts)
|
|
45
|
-
render_group_rmhost_events(result.events)
|
|
46
|
-
fmt.puts 2, '- all OK'
|
|
47
|
-
return result
|
|
48
|
-
end
|
|
49
|
-
rescue db.exceptions[:moose] => e
|
|
50
|
-
abort("ERROR: #{e.message}")
|
|
41
|
+
operation = build_operation(Moose::Inventory::Operations::RemoveAssociations)
|
|
42
|
+
run_group_relation_transaction(heading: "Dissociate group '#{name}' from host(s) '#{hosts.join(',')}':") do
|
|
43
|
+
group = fetch_existing_group_or_abort(name)
|
|
44
|
+
result = operation.group_from_hosts(group: group, group_name: name, host_names: hosts,
|
|
45
|
+
dry_run: options[:dry_run])
|
|
46
|
+
render_group_rmhost_events(result.events) unless machine_plan_output_requested?
|
|
47
|
+
result
|
|
51
48
|
end
|
|
52
49
|
end
|
|
53
50
|
|
|
54
|
-
def fetch_existing_group_for_rmhost(context, name)
|
|
55
|
-
fmt.puts 2, "- retrieve group '#{name}'..."
|
|
56
|
-
group = context.find_group(name)
|
|
57
|
-
abort("ERROR: The group '#{name}' does not exist.") if group.nil?
|
|
58
|
-
|
|
59
|
-
fmt.puts 4, '- OK'
|
|
60
|
-
group
|
|
61
|
-
end
|
|
62
|
-
|
|
63
51
|
def render_group_rmhost_events(events)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def render_group_rmhost_event(event)
|
|
68
|
-
payload = event.payload
|
|
69
|
-
|
|
70
|
-
return render_group_rmhost_warning(payload) if event.type == :group_host_association_missing
|
|
71
|
-
return render_group_rmhost_missing(payload) if event.type == :missing_skipping
|
|
72
|
-
|
|
73
|
-
case event.type
|
|
74
|
-
when :removing_group_host_association
|
|
75
|
-
fmt.puts 2, "- remove association {group:#{payload[:group]} <-> host:#{payload[:host]}}..."
|
|
76
|
-
when :adding_automatic_group
|
|
77
|
-
fmt.puts 2, "- add automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
78
|
-
when :ok
|
|
79
|
-
fmt.puts payload[:indent], '- OK'
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def render_group_rmhost_warning(payload)
|
|
84
|
-
fmt.warn "Association {group:#{payload[:group]} <-> host:#{payload[:host]}} doesn't exist, skipping.\n"
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def render_group_rmhost_missing(payload)
|
|
88
|
-
fmt.puts payload[:indent], "- doesn't exist, skipping."
|
|
52
|
+
emitter = host_group_association_removal_emitter(perspective: :group)
|
|
53
|
+
events.each { |event| emitter.call(event) }
|
|
89
54
|
end
|
|
90
55
|
end
|
|
91
56
|
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative '../inventory_context'
|
|
5
|
+
require_relative '../operations/remove_variables'
|
|
3
6
|
|
|
4
7
|
module Moose
|
|
5
8
|
module Inventory
|
|
@@ -10,54 +13,40 @@ module Moose
|
|
|
10
13
|
#==========================
|
|
11
14
|
desc 'rmvar NAME VARNAME',
|
|
12
15
|
'Remove a variable VARNAME from the group NAME'
|
|
16
|
+
option :dry_run, type: :boolean
|
|
17
|
+
option :yes, type: :boolean, desc: 'Confirm destructive removal without prompting'
|
|
18
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
13
19
|
def rmvar(*args)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"#{args.length} for 2 or more.")
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Convenience
|
|
20
|
-
db = Moose::Inventory::DB
|
|
21
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
20
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
21
|
+
validate_machine_plan_request!
|
|
22
22
|
|
|
23
|
-
# Arguments
|
|
24
23
|
name = args[0].downcase
|
|
25
24
|
vars = args.slice(1, args.length - 1).uniq
|
|
25
|
+
confirm_destructive_action!("group rmvar #{name} #{vars.join(',')}")
|
|
26
|
+
operation = build_operation(Moose::Inventory::Operations::RemoveVariables,
|
|
27
|
+
entity_type: :group,
|
|
28
|
+
emitter: machine_plan_emitter(group_rmvar_emitter(name, vars)))
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
result = db.transaction do
|
|
31
|
+
operation.call(name: name, vars: vars, dry_run: options[:dry_run])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
return if machine_plan_output_rendered?(result, command: 'group rmvar')
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"The group '#{name}' does not exist."
|
|
36
|
-
end
|
|
37
|
-
fmt.puts 4, '- OK'
|
|
36
|
+
record_audit({ command: 'group rmvar', action: 'remove_variable', entity_type: 'group',
|
|
37
|
+
entity_names: name }, result: result, dry_run: options[:dry_run])
|
|
38
|
+
print_success_summary
|
|
39
|
+
end
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
vars.each do |v|
|
|
41
|
-
fmt.puts 2, "- remove variable '#{v}'..."
|
|
42
|
-
vararray = v.split('=')
|
|
43
|
-
if v.start_with?('=') || v.scan('=').count > 1
|
|
44
|
-
fail db.exceptions[:moose],
|
|
45
|
-
"Incorrect format in {#{v}}. " \
|
|
46
|
-
'Expected \'key\' or \'key=value\'.'
|
|
47
|
-
end
|
|
41
|
+
private
|
|
48
42
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
fmt.puts 4, '- OK'
|
|
57
|
-
end
|
|
58
|
-
fmt.puts 2, '- all OK'
|
|
59
|
-
end # Transaction end
|
|
60
|
-
puts 'Succeeded.'
|
|
43
|
+
def group_rmvar_emitter(name, vars)
|
|
44
|
+
variable_operation_emitter(
|
|
45
|
+
action: :remove,
|
|
46
|
+
entity_label: 'group',
|
|
47
|
+
entity_name: name,
|
|
48
|
+
variables_label: vars.join(',')
|
|
49
|
+
)
|
|
61
50
|
end
|
|
62
51
|
end
|
|
63
52
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moose
|
|
4
|
+
module Inventory
|
|
5
|
+
module Cli
|
|
6
|
+
class Group
|
|
7
|
+
desc 'addtag GROUP TAG_1 [TAG_2 ...]', 'Add metadata tags to a group'
|
|
8
|
+
def addtag(*args)
|
|
9
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
10
|
+
|
|
11
|
+
add_tags('group', args[0].downcase, args.slice(1, args.length - 1))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc 'rmtag GROUP TAG_1 [TAG_2 ...]', 'Remove metadata tags from a group'
|
|
15
|
+
option :yes, type: :boolean, desc: 'Confirm destructive tag removal without prompting'
|
|
16
|
+
def rmtag(*args)
|
|
17
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
18
|
+
confirm_destructive_action!("group rmtag #{args[0].downcase} #{args.slice(1, args.length - 1).join(',')}")
|
|
19
|
+
|
|
20
|
+
remove_tags('group', args[0].downcase, args.slice(1, args.length - 1))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc 'listtags GROUP', 'List metadata tags for a group'
|
|
24
|
+
option :format, type: :string, desc: 'Emit tags as yaml|json|pjson'
|
|
25
|
+
def listtags(*args)
|
|
26
|
+
abort_if_missing_args(args, 1, '1')
|
|
27
|
+
|
|
28
|
+
list_tags('group', args[0].downcase)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../inventory_context'
|
|
4
|
+
require_relative 'audit_recording'
|
|
5
|
+
require_relative 'association_rendering'
|
|
6
|
+
require_relative 'child_relation_rendering'
|
|
7
|
+
require_relative 'factory'
|
|
8
|
+
require_relative 'listvars_support'
|
|
9
|
+
require_relative 'plan_rendering'
|
|
10
|
+
require_relative 'relation_transaction_support'
|
|
11
|
+
require_relative 'tag_support'
|
|
12
|
+
require_relative 'variable_rendering'
|
|
13
|
+
|
|
3
14
|
module Moose
|
|
4
15
|
module Inventory
|
|
5
16
|
module Cli
|
|
6
17
|
##
|
|
7
18
|
# Shared helpers for Thor command classes.
|
|
8
19
|
module Helpers
|
|
20
|
+
include Moose::Inventory::Cli::AssociationRendering
|
|
21
|
+
include Moose::Inventory::Cli::AuditRecording
|
|
22
|
+
include Moose::Inventory::Cli::ChildRelationRendering
|
|
23
|
+
include Moose::Inventory::Cli::ListvarsSupport
|
|
24
|
+
include Moose::Inventory::Cli::PlanRendering
|
|
25
|
+
include Moose::Inventory::Cli::RelationTransactionSupport
|
|
26
|
+
include Moose::Inventory::Cli::TagSupport
|
|
27
|
+
include Moose::Inventory::Cli::VariableRendering
|
|
28
|
+
|
|
9
29
|
AUTOMATIC_GROUP = 'ungrouped'
|
|
10
30
|
|
|
11
31
|
private
|
|
@@ -14,10 +34,38 @@ module Moose
|
|
|
14
34
|
Moose::Inventory::DB
|
|
15
35
|
end
|
|
16
36
|
|
|
37
|
+
def inventory_context
|
|
38
|
+
@inventory_context ||= Moose::Inventory::InventoryContext.new(db: db)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def cli_factory
|
|
42
|
+
@cli_factory ||= Moose::Inventory::Cli::Factory.new(context: inventory_context)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def build_operation(operation_class, **)
|
|
46
|
+
cli_factory.operation(operation_class, **)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def inventory_query
|
|
50
|
+
cli_factory.query_inventory
|
|
51
|
+
end
|
|
52
|
+
|
|
17
53
|
def fmt
|
|
18
54
|
Moose::Inventory::Cli::Formatter
|
|
19
55
|
end
|
|
20
56
|
|
|
57
|
+
def runtime_options
|
|
58
|
+
Moose::Inventory::Config.runtime_options
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def output_format
|
|
62
|
+
runtime_options.output_format
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def ansible_mode?
|
|
66
|
+
runtime_options.ansible?
|
|
67
|
+
end
|
|
68
|
+
|
|
21
69
|
def normalize_names(values)
|
|
22
70
|
values.uniq.map(&:downcase)
|
|
23
71
|
end
|
|
@@ -38,12 +86,31 @@ module Moose
|
|
|
38
86
|
abort(message || "ERROR: Cannot manually manipulate the automatic group '#{AUTOMATIC_GROUP}'.")
|
|
39
87
|
end
|
|
40
88
|
|
|
89
|
+
def confirm_destructive_action!(description)
|
|
90
|
+
return if options[:dry_run] || options[:yes]
|
|
91
|
+
|
|
92
|
+
abort("ERROR: #{description} is destructive. Re-run with --yes to confirm, or use --dry-run to preview.")
|
|
93
|
+
end
|
|
94
|
+
|
|
41
95
|
def association_exists?(dataset, name)
|
|
42
96
|
!dataset.nil? && !dataset[name: name].nil?
|
|
43
97
|
end
|
|
44
98
|
|
|
99
|
+
def exception_to_s(error)
|
|
100
|
+
error.to_s
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def print_warning_summary(result, success_message: 'Succeeded.', warning_message: 'Succeeded, with warnings.')
|
|
104
|
+
warning_count = result.respond_to?(:warning_count) ? result.warning_count : 0
|
|
105
|
+
print_success_summary(warning_count.zero? ? success_message : warning_message)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def print_success_summary(message = 'Succeeded.')
|
|
109
|
+
puts message
|
|
110
|
+
end
|
|
111
|
+
|
|
45
112
|
def automatic_group
|
|
46
|
-
|
|
113
|
+
inventory_context.automatic_group
|
|
47
114
|
end
|
|
48
115
|
|
|
49
116
|
def remove_automatic_group_from_host(host, indent:, message:)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
require 'json'
|
|
3
5
|
|
|
4
|
-
require_relative '
|
|
5
|
-
require_relative '
|
|
6
|
-
require_relative '../db/exceptions
|
|
6
|
+
require_relative 'formatter'
|
|
7
|
+
require_relative 'helpers'
|
|
8
|
+
require_relative '../db/exceptions'
|
|
7
9
|
|
|
8
10
|
module Moose
|
|
9
11
|
module Inventory
|
|
@@ -22,6 +24,7 @@ module Moose
|
|
|
22
24
|
require_relative 'host_addvar'
|
|
23
25
|
require_relative 'host_listvars'
|
|
24
26
|
require_relative 'host_rmvar'
|
|
27
|
+
require_relative 'host_tags'
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
end
|
|
@@ -15,12 +15,28 @@ module Moose
|
|
|
15
15
|
##
|
|
16
16
|
# Class implementing the "host" methods of the CLI
|
|
17
17
|
class Host
|
|
18
|
+
ADD_HOST_EVENT_RENDERERS = {
|
|
19
|
+
host_started: :render_add_host_started,
|
|
20
|
+
creating_host: :render_add_host_creation,
|
|
21
|
+
host_exists: :render_add_host_exists_warning,
|
|
22
|
+
ok: :render_add_host_ok,
|
|
23
|
+
adding_association: :render_add_host_association,
|
|
24
|
+
group_missing_created: :render_add_host_missing_group_warning,
|
|
25
|
+
association_exists: :render_add_host_association_exists_warning,
|
|
26
|
+
adding_automatic_group: :render_add_host_automatic_group,
|
|
27
|
+
host_complete: :render_add_host_complete,
|
|
28
|
+
dry_run_summary: :render_dry_run_summary
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
18
31
|
#==========================
|
|
19
32
|
desc 'add HOSTNAME_1 [HOSTNAME_2 ...]',
|
|
20
33
|
'Add a hosts HOSTNAME_n to the inventory'
|
|
21
34
|
option :groups
|
|
35
|
+
option :dry_run, type: :boolean
|
|
36
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
22
37
|
def add(*argv)
|
|
23
38
|
abort_if_missing_args(argv, 1, '1 or more')
|
|
39
|
+
validate_machine_plan_request!
|
|
24
40
|
|
|
25
41
|
# Arguments
|
|
26
42
|
names = normalize_names(argv)
|
|
@@ -32,11 +48,14 @@ module Moose
|
|
|
32
48
|
# Sanity
|
|
33
49
|
abort_if_automatic_group(groups)
|
|
34
50
|
|
|
35
|
-
result = Moose::Inventory::Operations::AddHosts
|
|
36
|
-
.
|
|
37
|
-
|
|
51
|
+
result = build_operation(Moose::Inventory::Operations::AddHosts)
|
|
52
|
+
.call(names: names, groups: groups, dry_run: options[:dry_run])
|
|
53
|
+
return if machine_plan_output_rendered?(result, command: 'host add')
|
|
54
|
+
|
|
55
|
+
record_audit({ command: 'host add', action: 'add', entity_type: 'host',
|
|
56
|
+
entity_names: names }, result: result, dry_run: options[:dry_run])
|
|
38
57
|
render_add_hosts_events(result.events)
|
|
39
|
-
|
|
58
|
+
print_warning_summary(result, success_message: 'Succeeded', warning_message: 'Succeeded')
|
|
40
59
|
end
|
|
41
60
|
|
|
42
61
|
private
|
|
@@ -46,31 +65,52 @@ module Moose
|
|
|
46
65
|
events.each { |event| render_add_hosts_event(event) }
|
|
47
66
|
end
|
|
48
67
|
|
|
49
|
-
def render_add_hosts_event(event)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
def render_add_hosts_event(event)
|
|
69
|
+
renderer = ADD_HOST_EVENT_RENDERERS[event.type]
|
|
70
|
+
send(renderer, event.payload) unless renderer.nil?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def render_add_host_started(payload)
|
|
74
|
+
puts "Add host '#{payload[:name]}':"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def render_add_host_creation(payload)
|
|
78
|
+
fmt.puts 2, "- Creating host '#{payload[:name]}'..."
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def render_add_host_exists_warning(payload)
|
|
82
|
+
fmt.warn "The host '#{payload[:name]}' already exists, skipping creation.\n"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def render_add_host_ok(payload)
|
|
86
|
+
fmt.puts payload[:indent], '- OK'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def render_add_host_association(payload)
|
|
90
|
+
fmt.puts 2, "- Adding association {host:#{payload[:host]} <-> group:#{payload[:group]}}..."
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def render_add_host_missing_group_warning(payload)
|
|
94
|
+
fmt.warn "The group '#{payload[:name]}' doesn't exist, but will be created.\n"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def render_add_host_association_exists_warning(payload)
|
|
98
|
+
fmt.warn(
|
|
99
|
+
"Association {host:#{payload[:host]} <-> group:#{payload[:group]}} " \
|
|
100
|
+
"already exists, skipping creation.\n"
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def render_add_host_automatic_group(payload)
|
|
105
|
+
fmt.puts 2, "- Adding automatic association {host:#{payload[:host]} <-> group:#{payload[:group]}}..."
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def render_add_host_complete(_payload)
|
|
109
|
+
fmt.puts 2, '- All OK'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def render_dry_run_summary(_payload)
|
|
113
|
+
puts 'Dry run complete. No changes applied.'
|
|
74
114
|
end
|
|
75
115
|
end
|
|
76
116
|
end
|