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
|
@@ -14,8 +14,11 @@ module Moose
|
|
|
14
14
|
#==========================
|
|
15
15
|
desc 'add NAME', 'Add a group NAME to the inventory'
|
|
16
16
|
option :hosts
|
|
17
|
+
option :dry_run, type: :boolean
|
|
18
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
17
19
|
def add(*argv)
|
|
18
20
|
abort_if_missing_args(argv, 1, '1 or more')
|
|
21
|
+
validate_machine_plan_request!
|
|
19
22
|
|
|
20
23
|
names = normalize_names(argv)
|
|
21
24
|
hosts = csv_option_names(options[:hosts])
|
|
@@ -25,16 +28,14 @@ module Moose
|
|
|
25
28
|
"ERROR: Cannot manually manipulate the automatic group 'ungrouped'\n"
|
|
26
29
|
)
|
|
27
30
|
|
|
28
|
-
result = Moose::Inventory::Operations::AddGroups
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
render_add_groups_events(result.events)
|
|
31
|
+
result = build_operation(Moose::Inventory::Operations::AddGroups)
|
|
32
|
+
.call(names: names, hosts: hosts, dry_run: options[:dry_run])
|
|
33
|
+
return if machine_plan_output_rendered?(result, command: 'group add')
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
end
|
|
35
|
+
record_audit({ command: 'group add', action: 'add', entity_type: 'group',
|
|
36
|
+
entity_names: names }, result: result, dry_run: options[:dry_run])
|
|
37
|
+
render_add_groups_events(result.events)
|
|
38
|
+
print_warning_summary(result, success_message: 'Succeeded')
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
private
|
|
@@ -48,6 +49,7 @@ module Moose
|
|
|
48
49
|
|
|
49
50
|
return render_add_groups_event_puts(event.type, payload) if puts_event?(event.type)
|
|
50
51
|
return render_add_groups_event_warn(event.type, payload) if warn_event?(event.type)
|
|
52
|
+
return puts 'Dry run complete. No changes applied.' if event.type == :dry_run_summary
|
|
51
53
|
|
|
52
54
|
render_add_groups_event_fmt(event.type, payload)
|
|
53
55
|
end
|
|
@@ -14,8 +14,11 @@ module Moose
|
|
|
14
14
|
#==========================
|
|
15
15
|
desc 'addchild PARENTGROUP CHILDGROUP_1 [CHILDGROUP_2 ... ]',
|
|
16
16
|
'Associate one or more child-groups CHILDGROUP_n with PARENTGROUP'
|
|
17
|
+
option :dry_run, type: :boolean
|
|
18
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
17
19
|
def addchild(*argv)
|
|
18
20
|
abort_if_missing_args(argv, 2, '2 or more')
|
|
21
|
+
validate_machine_plan_request!
|
|
19
22
|
|
|
20
23
|
pname = argv[0].downcase
|
|
21
24
|
cnames = normalize_names(argv.slice(1, argv.length - 1))
|
|
@@ -23,81 +26,36 @@ module Moose
|
|
|
23
26
|
abort_if_automatic_group([pname] + cnames)
|
|
24
27
|
|
|
25
28
|
result = add_children_to_group(pname, cnames)
|
|
29
|
+
return if machine_plan_output_rendered?(result, command: 'group addchild')
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
puts 'Succeeded, with warnings.'
|
|
31
|
-
end
|
|
31
|
+
record_audit({ command: 'group addchild', action: 'associate_child', entity_type: 'group',
|
|
32
|
+
entity_names: pname }, result: result, dry_run: options[:dry_run])
|
|
33
|
+
print_warning_summary(result)
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
private
|
|
35
37
|
|
|
36
38
|
def add_children_to_group(parent_name, child_names)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return result
|
|
52
|
-
end
|
|
53
|
-
rescue db.exceptions[:moose] => e
|
|
54
|
-
abort("ERROR: #{e}")
|
|
39
|
+
operation = build_operation(Moose::Inventory::Operations::GroupChildRelations)
|
|
40
|
+
run_group_relation_transaction(
|
|
41
|
+
heading: "Associate parent group '#{parent_name}' with child group(s) '#{child_names.join(',')}':",
|
|
42
|
+
on_error: method(:exception_to_s)
|
|
43
|
+
) do
|
|
44
|
+
parent_group = fetch_existing_group_or_abort(parent_name)
|
|
45
|
+
result = operation.add_children(
|
|
46
|
+
parent_group: parent_group,
|
|
47
|
+
parent_name: parent_name,
|
|
48
|
+
child_names: child_names,
|
|
49
|
+
dry_run: options[:dry_run]
|
|
50
|
+
)
|
|
51
|
+
render_addchild_events(result.events) unless machine_plan_output_requested?
|
|
52
|
+
result
|
|
55
53
|
end
|
|
56
54
|
end
|
|
57
55
|
|
|
58
|
-
def fetch_existing_group_for_child_relation(context, name)
|
|
59
|
-
fmt.puts 2, "- retrieve group '#{name}'..."
|
|
60
|
-
group = context.find_group(name)
|
|
61
|
-
abort("ERROR: The group '#{name}' does not exist.") if group.nil?
|
|
62
|
-
|
|
63
|
-
fmt.puts 4, '- OK'
|
|
64
|
-
group
|
|
65
|
-
end
|
|
66
|
-
|
|
67
56
|
def render_addchild_events(events)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def render_addchild_event(event)
|
|
72
|
-
payload = event.payload
|
|
73
|
-
|
|
74
|
-
return render_addchild_warning(event.type, payload) if addchild_warning?(event.type)
|
|
75
|
-
return render_addchild_existing(payload) if event.type == :already_exists_skipping
|
|
76
|
-
|
|
77
|
-
case event.type
|
|
78
|
-
when :adding_child_association
|
|
79
|
-
fmt.puts 2, "- add association {group:#{payload[:parent]} <-> group:#{payload[:child]}}..."
|
|
80
|
-
when :child_group_creating_now
|
|
81
|
-
fmt.puts 4, '- child group does not exist, creating now...'
|
|
82
|
-
when :ok
|
|
83
|
-
fmt.puts payload[:indent], '- OK'
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def addchild_warning?(type)
|
|
88
|
-
%i[child_association_exists child_group_missing].include?(type)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def render_addchild_warning(type, payload)
|
|
92
|
-
if type == :child_association_exists
|
|
93
|
-
fmt.warn "Association {group:#{payload[:parent]} <-> group:#{payload[:child]}}} already exists, skipping.\n"
|
|
94
|
-
else
|
|
95
|
-
fmt.warn "Group '#{payload[:name]}' does not exist and will be created.\n"
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def render_addchild_existing(payload)
|
|
100
|
-
fmt.puts payload[:indent], '- already exists, skipping.'
|
|
57
|
+
emitter = addchild_emitter
|
|
58
|
+
events.each { |event| emitter.call(event) }
|
|
101
59
|
end
|
|
102
60
|
end
|
|
103
61
|
end
|
|
@@ -14,8 +14,11 @@ module Moose
|
|
|
14
14
|
#==========================
|
|
15
15
|
desc 'addhost NAME HOSTNAME',
|
|
16
16
|
'Associate a host HOSTNAME with the group NAME'
|
|
17
|
+
option :dry_run, type: :boolean
|
|
18
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
17
19
|
def addhost(*args)
|
|
18
20
|
abort_if_missing_args(args, 2, '2 or more')
|
|
21
|
+
validate_machine_plan_request!
|
|
19
22
|
|
|
20
23
|
name = args[0].downcase
|
|
21
24
|
hosts = normalize_names(args.slice(1, args.length - 1))
|
|
@@ -23,83 +26,29 @@ module Moose
|
|
|
23
26
|
abort_if_automatic_group([name])
|
|
24
27
|
|
|
25
28
|
result = add_hosts_to_group(name, hosts)
|
|
29
|
+
return if machine_plan_output_rendered?(result, command: 'group addhost')
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
puts 'Succeeded, with warnings.'
|
|
31
|
-
end
|
|
31
|
+
record_audit({ command: 'group addhost', action: 'associate', entity_type: 'group',
|
|
32
|
+
entity_names: name }, result: result, dry_run: options[:dry_run])
|
|
33
|
+
print_warning_summary(result)
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
private
|
|
35
37
|
|
|
36
38
|
def add_hosts_to_group(name, hosts)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
result = operation.group_to_hosts(group: group, group_name: name, host_names: hosts)
|
|
45
|
-
render_group_addhost_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}")
|
|
39
|
+
operation = build_operation(Moose::Inventory::Operations::AddAssociations)
|
|
40
|
+
run_group_relation_transaction(heading: "Associate group '#{name}' with host(s) '#{hosts.join(',')}':") do
|
|
41
|
+
group = fetch_existing_group_or_abort(name)
|
|
42
|
+
result = operation.group_to_hosts(group: group, group_name: name, host_names: hosts,
|
|
43
|
+
dry_run: options[:dry_run])
|
|
44
|
+
render_group_addhost_events(result.events) unless machine_plan_output_requested?
|
|
45
|
+
result
|
|
51
46
|
end
|
|
52
47
|
end
|
|
53
48
|
|
|
54
|
-
def fetch_existing_group_for_addhost(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
49
|
def render_group_addhost_events(events)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def render_group_addhost_event(event)
|
|
68
|
-
payload = event.payload
|
|
69
|
-
|
|
70
|
-
return render_group_addhost_warning(event.type, payload) if group_addhost_warning?(event.type)
|
|
71
|
-
return render_group_addhost_status(payload) if event.type == :already_exists_skipping
|
|
72
|
-
|
|
73
|
-
render_group_addhost_output(event.type, payload)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def group_addhost_warning?(type)
|
|
77
|
-
%i[group_host_association_exists host_missing_created].include?(type)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def render_group_addhost_warning(type, payload)
|
|
81
|
-
if type == :group_host_association_exists
|
|
82
|
-
fmt.warn "Association {group:#{payload[:group]} <-> host:#{payload[:host]}} already exists, skipping.\n"
|
|
83
|
-
else
|
|
84
|
-
fmt.warn "Host '#{payload[:name]}' does not exist and will be created.\n"
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def render_group_addhost_status(payload)
|
|
89
|
-
fmt.puts payload[:indent], '- already exists, skipping.'
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def render_group_addhost_output(type, payload)
|
|
93
|
-
case type
|
|
94
|
-
when :adding_group_host_association
|
|
95
|
-
fmt.puts 2, "- add association {group:#{payload[:group]} <-> host:#{payload[:host]}}..."
|
|
96
|
-
when :host_creating_now
|
|
97
|
-
fmt.puts 4, '- host does not exist, creating now...'
|
|
98
|
-
when :removing_automatic_group
|
|
99
|
-
fmt.puts 2, "- remove automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
100
|
-
when :ok
|
|
101
|
-
fmt.puts payload[:indent], '- OK'
|
|
102
|
-
end
|
|
50
|
+
emitter = host_group_association_addition_emitter(perspective: :group)
|
|
51
|
+
events.each { |event| emitter.call(event) }
|
|
103
52
|
end
|
|
104
53
|
end
|
|
105
54
|
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/add_variables'
|
|
3
6
|
|
|
4
7
|
module Moose
|
|
5
8
|
module Inventory
|
|
@@ -10,61 +13,38 @@ module Moose
|
|
|
10
13
|
#==========================
|
|
11
14
|
desc 'addvar NAME VARNAME=VALUE',
|
|
12
15
|
'Add a variable VARNAME with value VALUE to the group NAME'
|
|
16
|
+
option :dry_run, type: :boolean
|
|
17
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
13
18
|
def addvar(*args)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"#{args.length} for 2 or more.")
|
|
17
|
-
end
|
|
18
|
-
# Convenience
|
|
19
|
-
db = Moose::Inventory::DB
|
|
20
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
19
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
20
|
+
validate_machine_plan_request!
|
|
21
21
|
|
|
22
|
-
# Arguments
|
|
23
22
|
name = args[0].downcase
|
|
24
23
|
vars = args.slice(1, args.length - 1).uniq
|
|
24
|
+
operation = build_operation(Moose::Inventory::Operations::AddVariables,
|
|
25
|
+
entity_type: :group,
|
|
26
|
+
emitter: machine_plan_emitter(group_addvar_emitter(name, vars)))
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
fmt.puts 2, "- retrieve group '#{name}'..."
|
|
30
|
-
group = db.models[:group].find(name: name)
|
|
31
|
-
if group.nil?
|
|
32
|
-
fail db.exceptions[:moose],
|
|
33
|
-
"The group '#{name}' does not exist."
|
|
34
|
-
end
|
|
35
|
-
fmt.puts 4, '- OK'
|
|
28
|
+
result = db.transaction do
|
|
29
|
+
operation.call(name: name, vars: vars, dry_run: options[:dry_run])
|
|
30
|
+
end
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
vars.each do |v|
|
|
39
|
-
fmt.puts 2, "- add variable '#{v}'..."
|
|
40
|
-
vararray = v.split('=')
|
|
32
|
+
return if machine_plan_output_rendered?(result, command: 'group addvar')
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
record_audit({ command: 'group addvar', action: 'add_variable', entity_type: 'group',
|
|
35
|
+
entity_names: name }, result: result, dry_run: options[:dry_run])
|
|
36
|
+
print_success_summary
|
|
37
|
+
end
|
|
46
38
|
|
|
47
|
-
|
|
48
|
-
groupvar = groupvars_ds[name: vararray[0]]
|
|
49
|
-
if !groupvar.nil?
|
|
50
|
-
unless groupvar[:value] == vararray[1]
|
|
51
|
-
fmt.puts 4, '- already exists, applying as an update...'
|
|
52
|
-
update = db.models[:groupvar].find(id: groupvar[:id])
|
|
53
|
-
update[:value] = vararray[1]
|
|
54
|
-
update.save
|
|
55
|
-
end
|
|
56
|
-
else
|
|
57
|
-
# groupvar doesn't exist, so create and associate
|
|
58
|
-
groupvar = db.models[:groupvar].create(name: vararray[0],
|
|
59
|
-
value: vararray[1])
|
|
60
|
-
group.add_groupvar(groupvar)
|
|
61
|
-
end
|
|
62
|
-
fmt.puts 4, '- OK'
|
|
63
|
-
end
|
|
64
|
-
fmt.puts 2, '- all OK'
|
|
65
|
-
end # Transaction end
|
|
39
|
+
private
|
|
66
40
|
|
|
67
|
-
|
|
41
|
+
def group_addvar_emitter(name, vars)
|
|
42
|
+
variable_operation_emitter(
|
|
43
|
+
action: :add,
|
|
44
|
+
entity_label: 'group',
|
|
45
|
+
entity_name: name,
|
|
46
|
+
variables_label: vars.join(',')
|
|
47
|
+
)
|
|
68
48
|
end
|
|
69
49
|
end
|
|
70
50
|
end
|
|
@@ -1,54 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative '../inventory_context'
|
|
5
|
+
require_relative '../operations/query_inventory'
|
|
3
6
|
|
|
4
7
|
module Moose
|
|
5
8
|
module Inventory
|
|
6
9
|
module Cli
|
|
7
|
-
##
|
|
8
10
|
# Implementation of the "group get" method of the CLI
|
|
9
11
|
class Group
|
|
10
12
|
desc 'get GROUP_1 [GROUP_2 ...]', 'Get groups GROUP_n from the inventory'
|
|
11
|
-
def get(*argv)
|
|
12
|
-
if argv.empty?
|
|
13
|
-
abort('ERROR: Wrong number of arguments, '\
|
|
14
|
-
"#{argv.length} for 1 or more")
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Convenience
|
|
18
|
-
db = Moose::Inventory::DB
|
|
19
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
20
|
-
|
|
21
|
-
# Arguments
|
|
22
|
-
names = argv.uniq.map(&:downcase)
|
|
23
|
-
|
|
24
|
-
# Process
|
|
25
|
-
results = {}
|
|
26
|
-
names.each do |name|
|
|
27
|
-
group = db.models[:group].find(name: name)
|
|
28
|
-
|
|
29
|
-
next if group.nil?
|
|
30
|
-
hosts = group.hosts_dataset.map(:name)
|
|
31
|
-
|
|
32
|
-
children = group.children_dataset.map(:name)
|
|
33
|
-
|
|
34
|
-
groupvars = {}
|
|
35
|
-
group.groupvars_dataset.each do |gv|
|
|
36
|
-
groupvars[gv[:name].to_sym] = gv[:value]
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
results[group[:name].to_sym] = {}
|
|
40
|
-
results[group[:name].to_sym][:hosts] = hosts unless hosts.empty?
|
|
41
|
-
|
|
42
|
-
unless children.empty?
|
|
43
|
-
results[group[:name].to_sym][:children] = children
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
unless groupvars.empty?
|
|
47
|
-
results[group[:name].to_sym][:groupvars] = groupvars
|
|
48
|
-
end
|
|
49
|
-
end
|
|
13
|
+
def get(*argv)
|
|
14
|
+
abort("ERROR: Wrong number of arguments, #{argv.length} for 1 or more") if argv.empty?
|
|
50
15
|
|
|
51
|
-
|
|
16
|
+
names = normalize_names(argv)
|
|
17
|
+
fmt.dump(inventory_query.get_groups(names: names), output_format)
|
|
52
18
|
end
|
|
53
19
|
end
|
|
54
20
|
end
|
|
@@ -1,52 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative '../inventory_context'
|
|
5
|
+
require_relative '../operations/query_inventory'
|
|
3
6
|
|
|
4
7
|
module Moose
|
|
5
8
|
module Inventory
|
|
6
9
|
module Cli
|
|
7
|
-
##
|
|
8
10
|
# Implementation of the "group list" method of the CLI
|
|
9
11
|
class Group
|
|
10
|
-
#==========================
|
|
11
12
|
desc 'list',
|
|
12
13
|
'List the groups, together with any associated hosts and groupvars'
|
|
13
|
-
def list
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
confopts = Moose::Inventory::Config._confopts
|
|
17
|
-
|
|
18
|
-
# Process
|
|
19
|
-
results = {}
|
|
20
|
-
db.models[:group].all.each do |group|
|
|
21
|
-
hosts = group.hosts_dataset.map(:name)
|
|
22
|
-
|
|
23
|
-
# Hide the automatic ungrouped group, if it's empty
|
|
24
|
-
next if group[:name] == 'ungrouped' && hosts.empty?
|
|
25
|
-
|
|
26
|
-
children = group.children_dataset.map(:name)
|
|
27
|
-
|
|
28
|
-
groupvars = {}
|
|
29
|
-
group.groupvars_dataset.each do |gv|
|
|
30
|
-
groupvars[gv[:name].to_sym] = gv[:value]
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
results[group[:name].to_sym] = {}
|
|
34
|
-
unless hosts.empty? && (confopts[:ansible] != true)
|
|
35
|
-
results[group[:name].to_sym][:hosts] = hosts
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
unless children.empty?
|
|
39
|
-
results[group[:name].to_sym][:children] = children
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
next if groupvars.empty?
|
|
43
|
-
if confopts[:ansible] == true
|
|
44
|
-
results[group[:name].to_sym][:vars] = groupvars
|
|
45
|
-
else
|
|
46
|
-
results[group[:name].to_sym][:groupvars] = groupvars
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
Formatter.out(results)
|
|
14
|
+
def list
|
|
15
|
+
results = inventory_query.list_groups(ansible: ansible_mode?)
|
|
16
|
+
fmt.dump(results, output_format)
|
|
50
17
|
end
|
|
51
18
|
end
|
|
52
19
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
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 '../inventory_context'
|
|
7
|
+
require_relative '../operations/query_inventory'
|
|
6
8
|
|
|
7
9
|
module Moose
|
|
8
10
|
module Inventory
|
|
@@ -13,61 +15,13 @@ module Moose
|
|
|
13
15
|
#==========================
|
|
14
16
|
desc 'listvar', 'List all variables associated with the group'
|
|
15
17
|
def listvars(*argv)
|
|
16
|
-
|
|
17
|
-
confopts = Moose::Inventory::Config._confopts
|
|
18
|
-
|
|
19
|
-
# Note, the Ansible spects don't call for a "--group GROUPNAME" method.
|
|
20
|
-
# So, strictly, there is no Ansible compatibility for this method.
|
|
21
|
-
# Instead, the Ansible compatibility included herein is for consistency
|
|
22
|
-
# with the "hosts listvars" method, which services the Ansible
|
|
23
|
-
# "--host HOSTNAME" specs.
|
|
24
|
-
|
|
25
|
-
# sanity
|
|
26
|
-
if confopts[:ansible] == true
|
|
27
|
-
if argv.length != 1
|
|
28
|
-
abort('ERROR: Wrong number of arguments for Ansible mode, '\
|
|
29
|
-
"#{args.length} for 1.")
|
|
30
|
-
end
|
|
31
|
-
else
|
|
32
|
-
if argv.empty?
|
|
33
|
-
abort('ERROR: Wrong number of arguments, '\
|
|
34
|
-
"#{args.length} for 1 or more.")
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Convenience
|
|
39
|
-
db = Moose::Inventory::DB
|
|
40
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
41
|
-
|
|
42
|
-
# Arguments
|
|
43
|
-
names = argv.uniq.map(&:downcase)
|
|
18
|
+
validate_listvars_args(argv)
|
|
44
19
|
|
|
45
|
-
|
|
46
|
-
results =
|
|
20
|
+
names = normalize_names(argv)
|
|
21
|
+
results = inventory_query.list_group_vars(names: names, ansible: ansible_mode?)
|
|
22
|
+
warn_if_missing_ansible_listvars_entity(:group, names.first)
|
|
47
23
|
|
|
48
|
-
|
|
49
|
-
# This is the implementation per Ansible specs
|
|
50
|
-
name = names.first
|
|
51
|
-
group = db.models[:group].find(name: name)
|
|
52
|
-
if group.nil?
|
|
53
|
-
fmt.warn "The Group #{name} does not exist."
|
|
54
|
-
else
|
|
55
|
-
group.groupvars_dataset.each do |gv|
|
|
56
|
-
results[gv[:name].to_sym] = gv[:value]
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
else
|
|
60
|
-
# This our more flexible implementation
|
|
61
|
-
names.each do |name|
|
|
62
|
-
group = db.models[:group].find(name: name)
|
|
63
|
-
next if group.nil?
|
|
64
|
-
results[name.to_sym] = {}
|
|
65
|
-
group.groupvars_dataset.each do |gv|
|
|
66
|
-
results[name.to_sym][gv[:name].to_sym] = gv[:value]
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
fmt.dump(results)
|
|
24
|
+
fmt.dump(results, output_format)
|
|
71
25
|
end
|
|
72
26
|
end
|
|
73
27
|
end
|
|
@@ -16,10 +16,14 @@ module Moose
|
|
|
16
16
|
type: :boolean,
|
|
17
17
|
default: false,
|
|
18
18
|
desc: 'Also delete child groups that become orphaned'
|
|
19
|
+
option :dry_run, type: :boolean
|
|
20
|
+
option :yes, type: :boolean, desc: 'Confirm destructive removal without prompting'
|
|
21
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
19
22
|
desc 'rm NAME',
|
|
20
23
|
'Remove a group NAME from the inventory'
|
|
21
24
|
def rm(*argv)
|
|
22
25
|
abort_if_missing_args(argv, 1, '1 or more')
|
|
26
|
+
validate_machine_plan_request!
|
|
23
27
|
|
|
24
28
|
names = normalize_names(argv)
|
|
25
29
|
|
|
@@ -27,25 +31,22 @@ module Moose
|
|
|
27
31
|
names,
|
|
28
32
|
"Cannot manually manipulate the automatic group 'ungrouped'\n"
|
|
29
33
|
)
|
|
34
|
+
confirm_destructive_action!("group rm #{names.join(',')}")
|
|
30
35
|
|
|
31
36
|
result = remove_groups(names)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
else
|
|
36
|
-
puts 'Succeeded, with warnings.'
|
|
37
|
-
end
|
|
37
|
+
record_audit({ command: 'group rm', action: 'remove', entity_type: 'group',
|
|
38
|
+
entity_names: names }, result: result, dry_run: options[:dry_run])
|
|
39
|
+
print_warning_summary(result) unless machine_plan_output_requested?
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
private
|
|
41
43
|
|
|
42
44
|
def remove_groups(names)
|
|
43
|
-
|
|
44
|
-
operation = Moose::Inventory::Operations::RemoveGroups.new(context: context)
|
|
45
|
+
operation = build_operation(Moose::Inventory::Operations::RemoveGroups)
|
|
45
46
|
|
|
46
47
|
db.transaction do
|
|
47
|
-
result = operation.call(names: names, recursive: options[:recursive])
|
|
48
|
-
render_group_rm_events(result.events)
|
|
48
|
+
result = operation.call(names: names, recursive: options[:recursive], dry_run: options[:dry_run])
|
|
49
|
+
machine_plan_output_rendered?(result, command: 'group rm') || render_group_rm_events(result.events)
|
|
49
50
|
return result
|
|
50
51
|
end
|
|
51
52
|
end
|
|
@@ -59,6 +60,7 @@ module Moose
|
|
|
59
60
|
|
|
60
61
|
render_group_rm_warning(payload) if event.type == :group_missing
|
|
61
62
|
return render_group_rm_progress(event.type, payload) if group_rm_progress?(event.type)
|
|
63
|
+
return puts 'Dry run complete. No changes applied.' if event.type == :dry_run_summary
|
|
62
64
|
|
|
63
65
|
render_group_rm_status(event.type, payload)
|
|
64
66
|
end
|