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
|
@@ -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/remove_variables'
|
|
6
8
|
|
|
7
9
|
module Moose
|
|
8
10
|
module Inventory
|
|
@@ -12,56 +14,40 @@ module Moose
|
|
|
12
14
|
class Host
|
|
13
15
|
#==========================
|
|
14
16
|
desc 'rmvar', 'Remove a variable from the host'
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Convenience
|
|
24
|
-
db = Moose::Inventory::DB
|
|
25
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
17
|
+
option :dry_run, type: :boolean
|
|
18
|
+
option :yes, type: :boolean, desc: 'Confirm destructive removal without prompting'
|
|
19
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
20
|
+
def rmvar(*args)
|
|
21
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
22
|
+
validate_machine_plan_request!
|
|
26
23
|
|
|
27
|
-
# Arguments
|
|
28
24
|
name = args[0].downcase
|
|
29
25
|
vars = args.slice(1, args.length - 1).uniq
|
|
26
|
+
confirm_destructive_action!("host rmvar #{name} #{vars.join(',')}")
|
|
27
|
+
operation = build_operation(Moose::Inventory::Operations::RemoveVariables,
|
|
28
|
+
entity_type: :host,
|
|
29
|
+
emitter: machine_plan_emitter(host_rmvar_emitter(name, vars)))
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
result = db.transaction do
|
|
32
|
+
operation.call(name: name, vars: vars, dry_run: options[:dry_run])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
return if machine_plan_output_rendered?(result, command: 'host rmvar')
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"The host '#{name}' does not exist."
|
|
40
|
-
end
|
|
41
|
-
fmt.puts 4, '- OK'
|
|
37
|
+
record_audit({ command: 'host rmvar', action: 'remove_variable', entity_type: 'host',
|
|
38
|
+
entity_names: name }, result: result, dry_run: options[:dry_run])
|
|
39
|
+
print_success_summary
|
|
40
|
+
end
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
vars.each do |v|
|
|
45
|
-
fmt.puts 2, "- remove variable '#{v}'..."
|
|
46
|
-
vararray = v.split('=')
|
|
47
|
-
if v.start_with?('=') || v.scan('=').count > 1
|
|
48
|
-
fail db.exceptions[:moose],
|
|
49
|
-
"Incorrect format in {#{v}}. " \
|
|
50
|
-
'Expected \'key\' or \'key=value\'.'
|
|
51
|
-
end
|
|
42
|
+
private
|
|
52
43
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
fmt.puts 4, '- OK'
|
|
61
|
-
end
|
|
62
|
-
fmt.puts 2, '- all OK'
|
|
63
|
-
end # Transaction end
|
|
64
|
-
puts 'Succeeded.'
|
|
44
|
+
def host_rmvar_emitter(name, vars)
|
|
45
|
+
variable_operation_emitter(
|
|
46
|
+
action: :remove,
|
|
47
|
+
entity_label: 'host',
|
|
48
|
+
entity_name: name,
|
|
49
|
+
variables_label: vars.join(',')
|
|
50
|
+
)
|
|
65
51
|
end
|
|
66
52
|
end
|
|
67
53
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moose
|
|
4
|
+
module Inventory
|
|
5
|
+
module Cli
|
|
6
|
+
class Host
|
|
7
|
+
desc 'addtag HOST TAG_1 [TAG_2 ...]', 'Add metadata tags to a host'
|
|
8
|
+
def addtag(*args)
|
|
9
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
10
|
+
|
|
11
|
+
add_tags('host', args[0].downcase, args.slice(1, args.length - 1))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc 'rmtag HOST TAG_1 [TAG_2 ...]', 'Remove metadata tags from a host'
|
|
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!("host rmtag #{args[0].downcase} #{args.slice(1, args.length - 1).join(',')}")
|
|
19
|
+
|
|
20
|
+
remove_tags('host', args[0].downcase, args.slice(1, args.length - 1))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc 'listtags HOST', 'List metadata tags for a host'
|
|
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('host', args[0].downcase)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moose
|
|
4
|
+
module Inventory
|
|
5
|
+
module Cli
|
|
6
|
+
# Shared argument and warning helpers for host/group listvars commands.
|
|
7
|
+
module ListvarsSupport
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def validate_listvars_args(args)
|
|
11
|
+
if ansible_mode?
|
|
12
|
+
abort_if_wrong_ansible_listvars_arg_count(args, 1)
|
|
13
|
+
else
|
|
14
|
+
abort_if_missing_args(args, 1, '1 or more')
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def warn_if_missing_ansible_listvars_entity(entity_type, name)
|
|
19
|
+
return unless ansible_mode?
|
|
20
|
+
return unless missing_listvars_entity?(entity_type, name)
|
|
21
|
+
|
|
22
|
+
fmt.warn missing_ansible_listvars_warning(entity_type, name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def abort_if_wrong_ansible_listvars_arg_count(args, expected)
|
|
26
|
+
return if args.length == expected
|
|
27
|
+
|
|
28
|
+
abort("ERROR: Wrong number of arguments for Ansible mode, #{args.length} for #{expected}.")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def missing_listvars_entity?(entity_type, name)
|
|
32
|
+
case entity_type
|
|
33
|
+
when :host
|
|
34
|
+
inventory_context.find_host(name).nil?
|
|
35
|
+
when :group
|
|
36
|
+
inventory_context.find_group(name).nil?
|
|
37
|
+
else
|
|
38
|
+
raise ArgumentError, "Unsupported listvars entity type: #{entity_type.inspect}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def missing_ansible_listvars_warning(entity_type, name)
|
|
43
|
+
case entity_type
|
|
44
|
+
when :host
|
|
45
|
+
"The host #{name} does not exist.\n"
|
|
46
|
+
when :group
|
|
47
|
+
"The Group #{name} does not exist."
|
|
48
|
+
else
|
|
49
|
+
raise ArgumentError, "Unsupported listvars entity type: #{entity_type.inspect}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moose
|
|
4
|
+
module Inventory
|
|
5
|
+
module Cli
|
|
6
|
+
# Shared machine-readable dry-run plan rendering helpers.
|
|
7
|
+
module PlanRendering
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def machine_plan_output_requested?
|
|
11
|
+
respond_to?(:options) && !options[:plan_format].nil?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def validate_machine_plan_request!
|
|
15
|
+
abort('ERROR: --plan-format requires --dry-run.') if machine_plan_output_requested? && !options[:dry_run]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def machine_plan_emitter(emitter)
|
|
19
|
+
machine_plan_output_requested? ? nil : emitter
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def machine_plan_output_rendered?(result, command:)
|
|
23
|
+
return false unless machine_plan_output_requested?
|
|
24
|
+
|
|
25
|
+
fmt.dump(
|
|
26
|
+
{
|
|
27
|
+
command: command,
|
|
28
|
+
dry_run: true,
|
|
29
|
+
changes_applied: false,
|
|
30
|
+
events: result.events.map { |event| serialize_plan_event(event) }
|
|
31
|
+
},
|
|
32
|
+
options[:plan_format].downcase
|
|
33
|
+
)
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def serialize_plan_event(event)
|
|
38
|
+
{
|
|
39
|
+
type: event.type.to_s,
|
|
40
|
+
payload: stringify_plan_payload(event.payload)
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def stringify_plan_payload(payload)
|
|
45
|
+
payload.to_h.transform_keys(&:to_s)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moose
|
|
4
|
+
module Inventory
|
|
5
|
+
module Cli
|
|
6
|
+
# Shared transaction/fetch helpers for host/group relation commands.
|
|
7
|
+
module RelationTransactionSupport
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def fetch_existing_group_or_abort(name)
|
|
11
|
+
fmt.puts 2, "- retrieve group '#{name}'..." unless machine_plan_output_requested?
|
|
12
|
+
group = inventory_context.find_group(name)
|
|
13
|
+
abort("ERROR: The group '#{name}' does not exist.") if group.nil?
|
|
14
|
+
|
|
15
|
+
fmt.puts 4, '- OK' unless machine_plan_output_requested?
|
|
16
|
+
group
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def fetch_existing_host_or_raise(name)
|
|
20
|
+
fmt.puts 2, "- Retrieve host '#{name}'..." unless machine_plan_output_requested?
|
|
21
|
+
host = inventory_context.find_host(name)
|
|
22
|
+
raise db.exceptions[:moose], "The host '#{name}' was not found in the database." if host.nil?
|
|
23
|
+
|
|
24
|
+
fmt.puts 4, '- OK' unless machine_plan_output_requested?
|
|
25
|
+
host
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def run_group_relation_transaction(heading:, on_error: nil, &)
|
|
29
|
+
run_relation_transaction(heading: heading, all_ok_message: '- all OK', on_error: on_error, &)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def run_host_relation_transaction(heading:, on_error: nil, &)
|
|
33
|
+
run_relation_transaction(heading: heading, all_ok_message: '- All OK', on_error: on_error, &)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def run_relation_transaction(heading:, all_ok_message:, on_error: nil)
|
|
37
|
+
result = nil
|
|
38
|
+
db.transaction do
|
|
39
|
+
puts heading unless machine_plan_output_requested?
|
|
40
|
+
result = yield
|
|
41
|
+
fmt.puts 2, all_ok_message unless machine_plan_output_requested?
|
|
42
|
+
end
|
|
43
|
+
result
|
|
44
|
+
rescue db.exceptions[:moose] => e
|
|
45
|
+
message = on_error ? on_error.call(e) : e.message
|
|
46
|
+
abort("ERROR: #{message}")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moose
|
|
4
|
+
module Inventory
|
|
5
|
+
module Cli
|
|
6
|
+
# Shared host/group metadata tag commands.
|
|
7
|
+
module TagSupport
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def add_tags(entity_type, entity_name, tag_names)
|
|
11
|
+
entity = fetch_tag_entity(entity_type, entity_name)
|
|
12
|
+
normalized = normalize_tags(tag_names)
|
|
13
|
+
changed = []
|
|
14
|
+
|
|
15
|
+
db.transaction do
|
|
16
|
+
normalized.each do |tag_name|
|
|
17
|
+
tag = inventory_context.find_or_create_tag(tag_name)
|
|
18
|
+
next unless entity.tags_dataset[name: tag_name].nil?
|
|
19
|
+
|
|
20
|
+
entity.add_tag(tag)
|
|
21
|
+
changed << tag_name
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
result = tag_result(events: changed.map { |tag| tag_event(:tag_added, entity_type, entity_name, tag) })
|
|
26
|
+
record_tag_audit(command: 'addtag', action: 'add_tag', entity_type: entity_type,
|
|
27
|
+
entity_name: entity_name, result: result, changed: changed)
|
|
28
|
+
puts "Added #{entity_type} tag(s) to '#{entity_name}': #{changed.join(', ')}."
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def remove_tags(entity_type, entity_name, tag_names)
|
|
32
|
+
entity = fetch_tag_entity(entity_type, entity_name)
|
|
33
|
+
normalized = normalize_tags(tag_names)
|
|
34
|
+
changed = []
|
|
35
|
+
|
|
36
|
+
db.transaction do
|
|
37
|
+
normalized.each do |tag_name|
|
|
38
|
+
tag = entity.tags_dataset[name: tag_name]
|
|
39
|
+
next if tag.nil?
|
|
40
|
+
|
|
41
|
+
entity.remove_tag(tag)
|
|
42
|
+
changed << tag_name
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
result = tag_result(events: changed.map { |tag| tag_event(:tag_removed, entity_type, entity_name, tag) })
|
|
47
|
+
record_tag_audit(command: 'rmtag', action: 'remove_tag', entity_type: entity_type,
|
|
48
|
+
entity_name: entity_name, result: result, changed: changed)
|
|
49
|
+
puts "Removed #{entity_type} tag(s) from '#{entity_name}': #{changed.join(', ')}."
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def list_tags(entity_type, entity_name)
|
|
53
|
+
entity = fetch_tag_entity(entity_type, entity_name)
|
|
54
|
+
tags = entity.tags_dataset.order(:name).map(:name)
|
|
55
|
+
|
|
56
|
+
if options[:format]
|
|
57
|
+
fmt.dump({ entity_type => entity_name, tags: tags }, options[:format].downcase)
|
|
58
|
+
elsif tags.empty?
|
|
59
|
+
puts "#{entity_type.capitalize} '#{entity_name}' has no tags."
|
|
60
|
+
else
|
|
61
|
+
puts "#{entity_type.capitalize} '#{entity_name}' tags: #{tags.join(', ')}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def fetch_tag_entity(entity_type, entity_name)
|
|
66
|
+
entity = inventory_context.public_send("find_#{entity_type}", entity_name)
|
|
67
|
+
abort("ERROR: The #{entity_type} '#{entity_name}' does not exist.") if entity.nil?
|
|
68
|
+
|
|
69
|
+
entity
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def normalize_tags(values)
|
|
73
|
+
inventory_context.normalize_tag_names(values)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def tag_result(events:)
|
|
77
|
+
Moose::Inventory::Operations::OperationEventSupport::Result.new(events: events, warning_count: 0)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def tag_event(type, entity_type, entity_name, tag_name)
|
|
81
|
+
Moose::Inventory::Operations::OperationEventSupport::Event.new(
|
|
82
|
+
type: type,
|
|
83
|
+
payload: { entity_type: entity_type, entity_name: entity_name, tag: tag_name }
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def record_tag_audit(metadata)
|
|
88
|
+
return if metadata.fetch(:changed).empty?
|
|
89
|
+
|
|
90
|
+
record_audit({ command: "#{metadata.fetch(:entity_type)} #{metadata.fetch(:command)}",
|
|
91
|
+
action: metadata.fetch(:action), entity_type: metadata.fetch(:entity_type),
|
|
92
|
+
entity_names: metadata.fetch(:entity_name) }, result: metadata.fetch(:result))
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moose
|
|
4
|
+
module Inventory
|
|
5
|
+
module Cli
|
|
6
|
+
# Shared rendering helpers for variable add/remove commands.
|
|
7
|
+
module VariableRendering
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def variable_operation_emitter(action:, entity_label:, entity_name:, variables_label:)
|
|
11
|
+
lambda do |event|
|
|
12
|
+
render_variable_event(
|
|
13
|
+
event,
|
|
14
|
+
action: action,
|
|
15
|
+
entity_label: entity_label,
|
|
16
|
+
entity_name: entity_name,
|
|
17
|
+
variables_label: variables_label
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render_variable_event(event, action:, entity_label:, entity_name:, variables_label:)
|
|
23
|
+
if event.type == :entity_started
|
|
24
|
+
return puts(variable_operation_heading(action:, entity_label:, entity_name:,
|
|
25
|
+
variables_label:))
|
|
26
|
+
end
|
|
27
|
+
return render_variable_change(event, entity_label) if variable_change_event?(event.type)
|
|
28
|
+
return puts 'Dry run complete. No changes applied.' if event.type == :dry_run_summary
|
|
29
|
+
|
|
30
|
+
render_variable_status(event)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def variable_operation_heading(action:, entity_label:, entity_name:, variables_label:)
|
|
34
|
+
return "Add variables '#{variables_label}' to #{entity_label} '#{entity_name}':" if action == :add
|
|
35
|
+
|
|
36
|
+
"Remove variable(s) '#{variables_label}' from #{entity_label} '#{entity_name}':"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def variable_change_event?(type)
|
|
40
|
+
%i[retrieving_entity adding_variable removing_variable updating_existing_variable].include?(type)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def render_variable_change(event, entity_label)
|
|
44
|
+
case event.type
|
|
45
|
+
when :retrieving_entity
|
|
46
|
+
fmt.puts 2, "- retrieve #{entity_label} '#{event.payload[:name]}'..."
|
|
47
|
+
when :adding_variable
|
|
48
|
+
fmt.puts 2, "- add variable '#{event.payload[:variable]}'..."
|
|
49
|
+
when :removing_variable
|
|
50
|
+
fmt.puts 2, "- remove variable '#{event.payload[:variable]}'..."
|
|
51
|
+
when :updating_existing_variable
|
|
52
|
+
fmt.puts 4, '- already exists, applying as an update...'
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def render_variable_status(event)
|
|
57
|
+
case event.type
|
|
58
|
+
when :entity_complete
|
|
59
|
+
fmt.puts 2, '- all OK'
|
|
60
|
+
when :ok
|
|
61
|
+
fmt.puts event.payload[:indent], '- OK'
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|