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,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
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative 'formatter'
|
|
5
|
+
require_relative '../inventory_context'
|
|
6
|
+
require_relative '../operations/remove_groups'
|
|
3
7
|
|
|
4
8
|
module Moose
|
|
5
9
|
module Inventory
|
|
@@ -8,82 +12,110 @@ module Moose
|
|
|
8
12
|
# Implementation of "group rm" methods of the CLI
|
|
9
13
|
class Group
|
|
10
14
|
#==========================
|
|
15
|
+
option :recursive,
|
|
16
|
+
type: :boolean,
|
|
17
|
+
default: false,
|
|
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'
|
|
11
22
|
desc 'rm NAME',
|
|
12
23
|
'Remove a group NAME from the inventory'
|
|
13
|
-
def rm(*argv)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
def rm(*argv)
|
|
25
|
+
abort_if_missing_args(argv, 1, '1 or more')
|
|
26
|
+
validate_machine_plan_request!
|
|
27
|
+
|
|
28
|
+
names = normalize_names(argv)
|
|
29
|
+
|
|
30
|
+
abort_if_automatic_group(
|
|
31
|
+
names,
|
|
32
|
+
"Cannot manually manipulate the automatic group 'ungrouped'\n"
|
|
33
|
+
)
|
|
34
|
+
confirm_destructive_action!("group rm #{names.join(',')}")
|
|
35
|
+
|
|
36
|
+
result = remove_groups(names)
|
|
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?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def remove_groups(names)
|
|
45
|
+
operation = build_operation(Moose::Inventory::Operations::RemoveGroups)
|
|
46
|
+
|
|
47
|
+
db.transaction do
|
|
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)
|
|
50
|
+
return result
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def render_group_rm_events(events)
|
|
55
|
+
events.each { |event| render_group_rm_event(event) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def render_group_rm_event(event)
|
|
59
|
+
payload = event.payload
|
|
60
|
+
|
|
61
|
+
render_group_rm_warning(payload) if event.type == :group_missing
|
|
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
|
|
64
|
+
|
|
65
|
+
render_group_rm_status(event.type, payload)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def group_rm_progress?(type)
|
|
69
|
+
%i[group_started retrieving_group removing_parent_association removing_child_association].include?(type)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def render_group_rm_warning(payload)
|
|
73
|
+
fmt.warn "Group '#{payload[:name]}' does not exist, skipping.\n"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def render_group_rm_progress(type, payload)
|
|
77
|
+
case type
|
|
78
|
+
when :group_started
|
|
79
|
+
puts "Remove group '#{payload[:name]}':"
|
|
80
|
+
when :retrieving_group
|
|
81
|
+
fmt.puts 2, "- Retrieve group '#{payload[:name]}'..."
|
|
82
|
+
when :removing_parent_association, :removing_child_association
|
|
83
|
+
fmt.puts 2, "- Remove association {group:#{payload[:group]} <-> group:#{payload[:related_group]}}..."
|
|
31
84
|
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def render_group_rm_status(type, payload)
|
|
88
|
+
return render_group_rm_secondary_status(type, payload) if group_rm_secondary_status?(type)
|
|
89
|
+
|
|
90
|
+
case type
|
|
91
|
+
when :group_missing
|
|
92
|
+
fmt.puts 4, '- No such group, skipping.'
|
|
93
|
+
when :destroying_group
|
|
94
|
+
fmt.puts payload[:indent], "- Destroy group '#{payload[:name]}'..."
|
|
95
|
+
when :group_complete
|
|
96
|
+
fmt.puts 2, '- All OK'
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def group_rm_secondary_status?(type)
|
|
101
|
+
%i[
|
|
102
|
+
recursively_delete_orphaned_group
|
|
103
|
+
removing_recursive_child_association
|
|
104
|
+
adding_automatic_group_to_host
|
|
105
|
+
ok
|
|
106
|
+
].include?(type)
|
|
107
|
+
end
|
|
32
108
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
fmt.puts 4, '- No such group, skipping.'
|
|
44
|
-
end
|
|
45
|
-
fmt.puts 4, '- OK'
|
|
46
|
-
unless group.nil?
|
|
47
|
-
# Dissociate from any parent groups
|
|
48
|
-
pgroups_ds = group.parents_dataset
|
|
49
|
-
pgroups_ds.each do |parent|
|
|
50
|
-
fmt.puts 2, "- Remove association {group:#{name} <-> group:#{parent.name}}..."
|
|
51
|
-
parent.remove_child(group)
|
|
52
|
-
fmt.puts 4, '- OK'
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Dissociate from any child groups
|
|
56
|
-
groups_ds = group.children_dataset
|
|
57
|
-
groups_ds.each do |child|
|
|
58
|
-
fmt.puts 2, "- Remove association {group:#{name} <-> group:#{child.name}}..."
|
|
59
|
-
group.remove_child(child)
|
|
60
|
-
# TODO: Should we propagate the delete to orphaned children?
|
|
61
|
-
fmt.puts 4, '- OK'
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Handle automatic group for any associated hosts
|
|
65
|
-
hosts_ds = group.hosts_dataset
|
|
66
|
-
hosts_ds.each do |host|
|
|
67
|
-
host_groups_ds = host.groups_dataset
|
|
68
|
-
next unless host_groups_ds.count == 1 # We're the only group
|
|
69
|
-
fmt.puts 2, "- Adding automatic association {group:ungrouped <-> host:#{host[:name]}}..."
|
|
70
|
-
ungrouped = db.models[:group].find_or_create(name: 'ungrouped')
|
|
71
|
-
host.add_group(ungrouped)
|
|
72
|
-
fmt.puts 4, '- OK'
|
|
73
|
-
end
|
|
74
|
-
# Remove the group
|
|
75
|
-
fmt.puts 2, "- Destroy group '#{name}'..."
|
|
76
|
-
group.remove_all_hosts
|
|
77
|
-
group.destroy
|
|
78
|
-
fmt.puts 4, '- OK'
|
|
79
|
-
end
|
|
80
|
-
fmt.puts 2, '- All OK'
|
|
81
|
-
end
|
|
82
|
-
end # Transaction end
|
|
83
|
-
if warn_count == 0
|
|
84
|
-
puts 'Succeeded.'
|
|
85
|
-
else
|
|
86
|
-
puts 'Succeeded, with warnings.'
|
|
109
|
+
def render_group_rm_secondary_status(type, payload)
|
|
110
|
+
case type
|
|
111
|
+
when :recursively_delete_orphaned_group
|
|
112
|
+
fmt.puts 2, "- Recursively delete orphaned group '#{payload[:name]}'..."
|
|
113
|
+
when :removing_recursive_child_association
|
|
114
|
+
fmt.puts 4, "- Remove association {group:#{payload[:parent]} <-> group:#{payload[:child]}}..."
|
|
115
|
+
when :adding_automatic_group_to_host
|
|
116
|
+
fmt.puts payload[:indent], "- Adding automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
117
|
+
when :ok
|
|
118
|
+
fmt.puts payload[:indent], '- OK'
|
|
87
119
|
end
|
|
88
120
|
end
|
|
89
121
|
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
|
|
3
|
-
require_relative '
|
|
5
|
+
require_relative 'formatter'
|
|
6
|
+
require_relative '../inventory_context'
|
|
7
|
+
require_relative '../operations/group_child_relations'
|
|
4
8
|
|
|
5
9
|
module Moose
|
|
6
10
|
module Inventory
|
|
@@ -9,72 +13,58 @@ module Moose
|
|
|
9
13
|
# Implemention of the "group rmchild" methods of the CLI
|
|
10
14
|
class Group
|
|
11
15
|
#==========================
|
|
16
|
+
option :delete_orphans,
|
|
17
|
+
type: :boolean,
|
|
18
|
+
default: false,
|
|
19
|
+
desc: 'Delete child groups that become orphaned'
|
|
12
20
|
desc 'rmchild PARENTGROUP CHILDGROUP_1 [CHILDGROUP_2 ... ]',
|
|
13
21
|
'Dissociate one or more child-groups CHILDGROUP_n from PARENTGROUP'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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'
|
|
25
|
+
def rmchild(*argv)
|
|
26
|
+
abort_if_missing_args(argv, 2, '2 or more')
|
|
27
|
+
validate_machine_plan_request!
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
cnames = args.slice(1, args.length - 1).uniq.map(&:downcase)
|
|
24
|
-
|
|
25
|
-
# Sanity
|
|
26
|
-
if pname == 'ungrouped' || cnames.include?('ungrouped')
|
|
27
|
-
abort("ERROR: Cannot manually manipulate the automatic group 'ungrouped'.")
|
|
28
|
-
end
|
|
29
|
+
pname = argv[0].downcase
|
|
30
|
+
cnames = normalize_names(argv.slice(1, argv.length - 1))
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
32
|
+
abort_if_automatic_group([pname] + cnames)
|
|
33
|
+
confirm_destructive_action!("group rmchild #{pname} #{cnames.join(',')}")
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
begin
|
|
37
|
-
db.transaction do # Transaction start
|
|
38
|
-
puts "Dissociate parent group '#{pname}' from child group(s) '#{cnames.join(',')}':"
|
|
39
|
-
# Get the target group
|
|
40
|
-
fmt.puts 2, "- retrieve group '#{pname}'..."
|
|
41
|
-
pgroup = db.models[:group].find(name: pname)
|
|
42
|
-
if pgroup.nil?
|
|
43
|
-
abort("ERROR: The group '#{pname}' does not exist.")
|
|
44
|
-
end
|
|
45
|
-
fmt.puts 4, '- OK'
|
|
35
|
+
result = remove_children_from_group(pname, cnames)
|
|
36
|
+
return if machine_plan_output_rendered?(result, command: 'group rmchild')
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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)
|
|
41
|
+
end
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
if groups_ds[name: cname].nil?
|
|
54
|
-
warn_count += 1
|
|
55
|
-
fmt.warn "Association {group:#{pname} <-> group:#{cname}}"\
|
|
56
|
-
" does not exist, skipping.\n"
|
|
57
|
-
fmt.puts 4, "- doesn't exist, skipping."
|
|
58
|
-
fmt.puts 4, '- OK'
|
|
59
|
-
next
|
|
60
|
-
end
|
|
43
|
+
private
|
|
61
44
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
45
|
+
def remove_children_from_group(parent_name, child_names)
|
|
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
|
|
76
61
|
end
|
|
77
62
|
end
|
|
63
|
+
|
|
64
|
+
def render_rmchild_events(events)
|
|
65
|
+
emitter = rmchild_emitter
|
|
66
|
+
events.each { |event| emitter.call(event) }
|
|
67
|
+
end
|
|
78
68
|
end
|
|
79
69
|
end
|
|
80
70
|
end
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative 'formatter'
|
|
5
|
+
require_relative '../inventory_context'
|
|
6
|
+
require_relative '../operations/remove_associations'
|
|
3
7
|
|
|
4
8
|
module Moose
|
|
5
9
|
module Inventory
|
|
@@ -10,75 +14,44 @@ module Moose
|
|
|
10
14
|
#==========================
|
|
11
15
|
desc 'rmhost GROUPNAME HOSTNAME_1 [HOSTNAME_2 ...]',
|
|
12
16
|
'Dissociate the hosts HOSTNAME_n from the group NAME'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
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'
|
|
20
|
+
def rmhost(*args)
|
|
21
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
22
|
+
validate_machine_plan_request!
|
|
20
23
|
|
|
21
|
-
# Arguments
|
|
22
24
|
name = args[0].downcase
|
|
23
|
-
hosts = args.slice(1, args.length - 1)
|
|
24
|
-
|
|
25
|
-
# Sanity
|
|
26
|
-
if name == 'ungrouped'
|
|
27
|
-
abort("ERROR: Cannot manually manipulate the automatic group 'ungrouped'.")
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Convenience
|
|
31
|
-
db = Moose::Inventory::DB
|
|
32
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
25
|
+
hosts = normalize_names(args.slice(1, args.length - 1))
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
begin
|
|
37
|
-
db.transaction do # Transaction start
|
|
38
|
-
# Get the target group
|
|
39
|
-
puts "Dissociate group '#{name}' from host(s) '#{hosts.join(',')}':"
|
|
40
|
-
fmt.puts 2, "- retrieve group '#{name}'..."
|
|
41
|
-
group = db.models[:group].find(name: name)
|
|
42
|
-
abort("ERROR: The group '#{name}' does not exist.") if group.nil?
|
|
43
|
-
fmt.puts 4, '- OK'
|
|
27
|
+
abort_if_automatic_group([name])
|
|
28
|
+
confirm_destructive_action!("group rmhost #{name} #{hosts.join(',')}")
|
|
44
29
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
hosts_ds = group.hosts_dataset
|
|
48
|
-
hosts.each do |h|
|
|
49
|
-
fmt.puts 2, "- remove association {group:#{name} <-> host:#{h}}..."
|
|
30
|
+
result = remove_hosts_from_group(name, hosts)
|
|
31
|
+
return if machine_plan_output_rendered?(result, command: 'group rmhost')
|
|
50
32
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
" exist, skipping.\n"
|
|
56
|
-
fmt.puts 4, '- doesn\'t exist, skipping.'
|
|
57
|
-
fmt.puts 4, '- OK'
|
|
58
|
-
next
|
|
59
|
-
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)
|
|
36
|
+
end
|
|
60
37
|
|
|
61
|
-
|
|
62
|
-
group.remove_host(host) unless host.nil?
|
|
63
|
-
fmt.puts 4, '- OK'
|
|
38
|
+
private
|
|
64
39
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
rescue db.exceptions[:moose] => e
|
|
74
|
-
abort("ERROR: #{e.message}")
|
|
75
|
-
end
|
|
76
|
-
if warn_count == 0
|
|
77
|
-
puts 'Succeeded.'
|
|
78
|
-
else
|
|
79
|
-
puts 'Succeeded, with warnings.'
|
|
40
|
+
def remove_hosts_from_group(name, hosts)
|
|
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
|
|
80
48
|
end
|
|
81
49
|
end
|
|
50
|
+
|
|
51
|
+
def render_group_rmhost_events(events)
|
|
52
|
+
emitter = host_group_association_removal_emitter(perspective: :group)
|
|
53
|
+
events.each { |event| emitter.call(event) }
|
|
54
|
+
end
|
|
82
55
|
end
|
|
83
56
|
end
|
|
84
57
|
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
|