openbolt 5.0.0.rc1
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 +7 -0
- data/Puppetfile +52 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +60 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +51 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +71 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +55 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +65 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +93 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +33 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +38 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +208 -0
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +62 -0
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +57 -0
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +130 -0
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +31 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +52 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +87 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +34 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +35 -0
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +74 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +97 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +47 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +52 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +40 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +42 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +106 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +291 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +145 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +164 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +211 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +48 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +43 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +145 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +38 -0
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +101 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +29 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +131 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +59 -0
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +39 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +50 -0
- data/bolt-modules/boltlib/types/planresult.pp +18 -0
- data/bolt-modules/boltlib/types/targetspec.pp +7 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +42 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +20 -0
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
- data/bolt-modules/file/lib/puppet/functions/file/delete.rb +21 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +28 -0
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +20 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +33 -0
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +28 -0
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +24 -0
- data/bolt-modules/log/lib/puppet/functions/log/debug.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/error.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/fatal.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/info.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/trace.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/warn.rb +41 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +36 -0
- data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +35 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +65 -0
- data/bolt-modules/system/lib/puppet/functions/system/env.rb +20 -0
- data/exe/bolt +17 -0
- data/guides/debugging.yaml +27 -0
- data/guides/inventory.yaml +23 -0
- data/guides/links.yaml +12 -0
- data/guides/logging.yaml +17 -0
- data/guides/module.yaml +18 -0
- data/guides/modulepath.yaml +24 -0
- data/guides/project.yaml +21 -0
- data/guides/targets.yaml +28 -0
- data/guides/transports.yaml +22 -0
- data/lib/bolt/analytics.rb +233 -0
- data/lib/bolt/application.rb +806 -0
- data/lib/bolt/applicator.rb +368 -0
- data/lib/bolt/apply_inventory.rb +93 -0
- data/lib/bolt/apply_result.rb +154 -0
- data/lib/bolt/apply_target.rb +90 -0
- data/lib/bolt/bolt_option_parser.rb +1226 -0
- data/lib/bolt/catalog/logging.rb +15 -0
- data/lib/bolt/catalog.rb +144 -0
- data/lib/bolt/cli.rb +949 -0
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +673 -0
- data/lib/bolt/config/transport/base.rb +133 -0
- data/lib/bolt/config/transport/docker.rb +34 -0
- data/lib/bolt/config/transport/jail.rb +33 -0
- data/lib/bolt/config/transport/local.rb +39 -0
- data/lib/bolt/config/transport/lxd.rb +34 -0
- data/lib/bolt/config/transport/options.rb +431 -0
- data/lib/bolt/config/transport/orch.rb +41 -0
- data/lib/bolt/config/transport/podman.rb +33 -0
- data/lib/bolt/config/transport/remote.rb +24 -0
- data/lib/bolt/config/transport/ssh.rb +138 -0
- data/lib/bolt/config/transport/winrm.rb +63 -0
- data/lib/bolt/config.rb +515 -0
- data/lib/bolt/container_result.rb +105 -0
- data/lib/bolt/error.rb +194 -0
- data/lib/bolt/executor.rb +539 -0
- data/lib/bolt/fiber_executor.rb +190 -0
- data/lib/bolt/inventory/group.rb +446 -0
- data/lib/bolt/inventory/inventory.rb +391 -0
- data/lib/bolt/inventory/options.rb +139 -0
- data/lib/bolt/inventory/target.rb +293 -0
- data/lib/bolt/inventory.rb +120 -0
- data/lib/bolt/logger.rb +252 -0
- data/lib/bolt/module.rb +54 -0
- data/lib/bolt/module_installer/installer.rb +44 -0
- data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
- data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
- data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
- data/lib/bolt/module_installer/puppetfile.rb +131 -0
- data/lib/bolt/module_installer/resolver.rb +129 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +91 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +150 -0
- data/lib/bolt/module_installer/specs/id/base.rb +116 -0
- data/lib/bolt/module_installer/specs/id/gitclone.rb +120 -0
- data/lib/bolt/module_installer/specs/id/github.rb +90 -0
- data/lib/bolt/module_installer/specs/id/gitlab.rb +92 -0
- data/lib/bolt/module_installer/specs.rb +95 -0
- data/lib/bolt/module_installer.rb +208 -0
- data/lib/bolt/node/errors.rb +55 -0
- data/lib/bolt/node/output.rb +29 -0
- data/lib/bolt/outputter/human.rb +958 -0
- data/lib/bolt/outputter/json.rb +205 -0
- data/lib/bolt/outputter/logger.rb +76 -0
- data/lib/bolt/outputter/rainbow.rb +118 -0
- data/lib/bolt/outputter.rb +57 -0
- data/lib/bolt/pal/issues.rb +19 -0
- data/lib/bolt/pal/logging.rb +17 -0
- data/lib/bolt/pal/yaml_plan/evaluator.rb +83 -0
- data/lib/bolt/pal/yaml_plan/loader.rb +94 -0
- data/lib/bolt/pal/yaml_plan/parameter.rb +63 -0
- data/lib/bolt/pal/yaml_plan/step/command.rb +45 -0
- data/lib/bolt/pal/yaml_plan/step/download.rb +37 -0
- data/lib/bolt/pal/yaml_plan/step/eval.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +31 -0
- data/lib/bolt/pal/yaml_plan/step/plan.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/resources.rb +170 -0
- data/lib/bolt/pal/yaml_plan/step/script.rb +62 -0
- data/lib/bolt/pal/yaml_plan/step/task.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +37 -0
- data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
- data/lib/bolt/pal/yaml_plan/step.rb +223 -0
- data/lib/bolt/pal/yaml_plan/transpiler.rb +90 -0
- data/lib/bolt/pal/yaml_plan.rb +172 -0
- data/lib/bolt/pal.rb +847 -0
- data/lib/bolt/plan_creator.rb +219 -0
- data/lib/bolt/plan_future.rb +86 -0
- data/lib/bolt/plan_result.rb +44 -0
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/env_var.rb +54 -0
- data/lib/bolt/plugin/module.rb +276 -0
- data/lib/bolt/plugin/prompt.rb +36 -0
- data/lib/bolt/plugin/puppet_connect_data.rb +84 -0
- data/lib/bolt/plugin/puppetdb.rb +124 -0
- data/lib/bolt/plugin/task.rb +72 -0
- data/lib/bolt/plugin.rb +380 -0
- data/lib/bolt/project.rb +219 -0
- data/lib/bolt/project_manager/config_migrator.rb +113 -0
- data/lib/bolt/project_manager/inventory_migrator.rb +67 -0
- data/lib/bolt/project_manager/migrator.rb +39 -0
- data/lib/bolt/project_manager/module_migrator.rb +203 -0
- data/lib/bolt/project_manager.rb +221 -0
- data/lib/bolt/puppetdb/client.rb +153 -0
- data/lib/bolt/puppetdb/config.rb +176 -0
- data/lib/bolt/puppetdb/instance.rb +146 -0
- data/lib/bolt/puppetdb.rb +15 -0
- data/lib/bolt/r10k_log_proxy.rb +30 -0
- data/lib/bolt/rerun.rb +55 -0
- data/lib/bolt/resource_instance.rb +133 -0
- data/lib/bolt/result.rb +247 -0
- data/lib/bolt/result_set.rb +128 -0
- data/lib/bolt/shell/bash/tmpdir.rb +62 -0
- data/lib/bolt/shell/bash.rb +516 -0
- data/lib/bolt/shell/powershell/snippets.rb +181 -0
- data/lib/bolt/shell/powershell.rb +365 -0
- data/lib/bolt/shell.rb +105 -0
- data/lib/bolt/target.rb +174 -0
- data/lib/bolt/task/puppet_server.rb +27 -0
- data/lib/bolt/task/run.rb +55 -0
- data/lib/bolt/task.rb +163 -0
- data/lib/bolt/transport/base.rb +252 -0
- data/lib/bolt/transport/docker/connection.rb +150 -0
- data/lib/bolt/transport/docker.rb +23 -0
- data/lib/bolt/transport/jail/connection.rb +81 -0
- data/lib/bolt/transport/jail.rb +21 -0
- data/lib/bolt/transport/local/connection.rb +106 -0
- data/lib/bolt/transport/local.rb +20 -0
- data/lib/bolt/transport/lxd/connection.rb +115 -0
- data/lib/bolt/transport/lxd.rb +26 -0
- data/lib/bolt/transport/orch/connection.rb +111 -0
- data/lib/bolt/transport/orch.rb +271 -0
- data/lib/bolt/transport/podman/connection.rb +102 -0
- data/lib/bolt/transport/podman.rb +19 -0
- data/lib/bolt/transport/remote.rb +41 -0
- data/lib/bolt/transport/simple.rb +54 -0
- data/lib/bolt/transport/ssh/connection.rb +321 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +140 -0
- data/lib/bolt/transport/ssh.rb +48 -0
- data/lib/bolt/transport/winrm/connection.rb +378 -0
- data/lib/bolt/transport/winrm.rb +33 -0
- data/lib/bolt/util/format.rb +68 -0
- data/lib/bolt/util/puppet_log_level.rb +21 -0
- data/lib/bolt/util.rb +465 -0
- data/lib/bolt/validator.rb +227 -0
- data/lib/bolt/version.rb +5 -0
- data/lib/bolt.rb +8 -0
- data/lib/bolt_server/acl.rb +39 -0
- data/lib/bolt_server/base_config.rb +112 -0
- data/lib/bolt_server/config.rb +64 -0
- data/lib/bolt_server/file_cache.rb +200 -0
- data/lib/bolt_server/request_error.rb +11 -0
- data/lib/bolt_server/schemas/action-check_node_connections.json +14 -0
- data/lib/bolt_server/schemas/action-run_command.json +12 -0
- data/lib/bolt_server/schemas/action-run_script.json +47 -0
- data/lib/bolt_server/schemas/action-run_task.json +20 -0
- data/lib/bolt_server/schemas/action-upload_file.json +47 -0
- data/lib/bolt_server/schemas/partials/target-any.json +10 -0
- data/lib/bolt_server/schemas/partials/target-ssh.json +88 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +67 -0
- data/lib/bolt_server/schemas/partials/task.json +94 -0
- data/lib/bolt_server/schemas/transport-ssh.json +25 -0
- data/lib/bolt_server/schemas/transport-winrm.json +19 -0
- data/lib/bolt_server/transport_app.rb +554 -0
- data/lib/bolt_spec/bolt_context.rb +226 -0
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +51 -0
- data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
- data/lib/bolt_spec/plans/action_stubs/plan_stub.rb +55 -0
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +59 -0
- data/lib/bolt_spec/plans/action_stubs/task_stub.rb +57 -0
- data/lib/bolt_spec/plans/action_stubs/upload_stub.rb +65 -0
- data/lib/bolt_spec/plans/action_stubs.rb +196 -0
- data/lib/bolt_spec/plans/mock_executor.rb +361 -0
- data/lib/bolt_spec/plans/publish_stub.rb +49 -0
- data/lib/bolt_spec/plans.rb +190 -0
- data/lib/bolt_spec/run.rb +246 -0
- data/lib/logging_extensions/logging.rb +13 -0
- data/libexec/apply_catalog.rb +130 -0
- data/libexec/bolt_catalog +68 -0
- data/libexec/custom_facts.rb +63 -0
- data/libexec/query_resources.rb +75 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/count.rb +21 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/nodes.rb +22 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/targets.rb +21 -0
- data/modules/aggregate/plans/count.pp +56 -0
- data/modules/aggregate/plans/targets.pp +56 -0
- data/modules/canary/lib/puppet/functions/canary/merge.rb +13 -0
- data/modules/canary/lib/puppet/functions/canary/random_split.rb +22 -0
- data/modules/canary/lib/puppet/functions/canary/skip.rb +25 -0
- data/modules/canary/plans/init.pp +100 -0
- data/modules/puppet_connect/plans/test_input_data.pp +94 -0
- data/modules/puppetdb_fact/plans/init.pp +20 -0
- data/resources/bolt_bash_completion.sh +214 -0
- metadata +735 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../bolt/resource_instance'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class Inventory
|
7
|
+
# This class represents the active state of a target within the inventory.
|
8
|
+
class Target
|
9
|
+
attr_reader :name, :uri, :safe_name, :target_alias, :resources
|
10
|
+
|
11
|
+
# Illegal characters that are not permitted in target names.
|
12
|
+
# These characters are delimiters for target and group names and allowing
|
13
|
+
# them would cause unexpected behavior.
|
14
|
+
ILLEGAL_CHARS = /[\s,]/.freeze
|
15
|
+
|
16
|
+
def initialize(target_data, inventory)
|
17
|
+
unless target_data['name'] || target_data['uri']
|
18
|
+
raise Bolt::Inventory::ValidationError.new("Target must have either a name or uri", nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
@logger = Bolt::Logger.logger(inventory)
|
22
|
+
|
23
|
+
# If the target isn't mentioned by any groups, it won't have a uri or
|
24
|
+
# name and we will use the target_name as both
|
25
|
+
@uri = target_data['uri']
|
26
|
+
@uri_obj = self.class.parse_uri(@uri)
|
27
|
+
|
28
|
+
# If the target has a name, use that as the safe name. Otherwise, turn
|
29
|
+
# the uri into a safe name by omitting the password.
|
30
|
+
if target_data['name']
|
31
|
+
@name = target_data['name']
|
32
|
+
@safe_name = target_data['name']
|
33
|
+
else
|
34
|
+
@name = @uri
|
35
|
+
@safe_name = @uri_obj.omit(:password).to_str.sub(%r{^//}, '')
|
36
|
+
end
|
37
|
+
# handle special localhost target
|
38
|
+
if @name == 'localhost'
|
39
|
+
default = { 'config' => { 'transport' => 'local' } }
|
40
|
+
target_data = Bolt::Util.deep_merge(default, target_data)
|
41
|
+
end
|
42
|
+
|
43
|
+
@config = target_data['config'] || {}
|
44
|
+
@vars = target_data['vars'] || {}
|
45
|
+
@facts = target_data['facts'] || {}
|
46
|
+
@features = target_data['features'] || Set.new
|
47
|
+
@plugin_hooks = target_data['plugin_hooks'] || {}
|
48
|
+
# When alias is specified in a plan, the key will be `target_alias`, when
|
49
|
+
# alias is specified in inventory the key will be `alias`.
|
50
|
+
@target_alias = target_data['target_alias'] || target_data['alias'] || []
|
51
|
+
@resources = {}
|
52
|
+
|
53
|
+
@inventory = inventory
|
54
|
+
|
55
|
+
validate
|
56
|
+
# after setting config, apply local defaults when using bundled ruby
|
57
|
+
set_local_defaults if transport_config['bundled-ruby']
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_local_defaults
|
61
|
+
return if @set_local_default
|
62
|
+
defaults = {
|
63
|
+
'local' => { 'interpreters' => { '.rb' => RbConfig.ruby } }
|
64
|
+
}
|
65
|
+
old_config = @config
|
66
|
+
@config = Bolt::Util.deep_merge(defaults, @config)
|
67
|
+
invalidate_config_cache! if old_config != @config
|
68
|
+
set_feature('puppet-agent')
|
69
|
+
@set_local_default = true
|
70
|
+
end
|
71
|
+
|
72
|
+
# rubocop:disable Naming/AccessorMethodName
|
73
|
+
def set_resource(resource)
|
74
|
+
if (existing_resource = resources[resource.reference])
|
75
|
+
existing_resource.overwrite_state(resource.state)
|
76
|
+
existing_resource.overwrite_desired_state(resource.desired_state)
|
77
|
+
existing_resource.events = existing_resource.events + resource.events
|
78
|
+
existing_resource
|
79
|
+
else
|
80
|
+
@resources[resource.reference] = resource
|
81
|
+
end
|
82
|
+
end
|
83
|
+
# rubocop:enable Naming/AccessorMethodName
|
84
|
+
|
85
|
+
def vars
|
86
|
+
group_cache['vars'].merge(@vars)
|
87
|
+
end
|
88
|
+
|
89
|
+
# This method isn't actually an accessor and we want the name to
|
90
|
+
# correspond to the Puppet function
|
91
|
+
# rubocop:disable Naming/AccessorMethodName
|
92
|
+
def set_var(var_hash)
|
93
|
+
@vars.merge!(var_hash)
|
94
|
+
end
|
95
|
+
# rubocop:enable Naming/AccessorMethodName
|
96
|
+
|
97
|
+
def facts
|
98
|
+
Bolt::Util.deep_merge(group_cache['facts'], @facts)
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_facts(new_facts = {})
|
102
|
+
validate_fact_names(new_facts)
|
103
|
+
Bolt::Util.deep_merge!(@facts, new_facts)
|
104
|
+
end
|
105
|
+
|
106
|
+
def features
|
107
|
+
group_cache['features'] + @features
|
108
|
+
end
|
109
|
+
|
110
|
+
def set_feature(feature, value = true)
|
111
|
+
if value
|
112
|
+
@features << feature
|
113
|
+
else
|
114
|
+
@features.delete(feature)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def resource(type, title)
|
119
|
+
resources[Bolt::ResourceInstance.format_reference(type, title)]
|
120
|
+
end
|
121
|
+
|
122
|
+
def plugin_hooks
|
123
|
+
# Merge plugin_hooks from the config file with any defined by the group
|
124
|
+
# or assigned dynamically to the target
|
125
|
+
@inventory.plugins.plugin_hooks.merge(group_cache['plugin_hooks']).merge(@plugin_hooks)
|
126
|
+
end
|
127
|
+
|
128
|
+
def set_config(key_or_key_path, value)
|
129
|
+
if key_or_key_path.empty?
|
130
|
+
@config = value
|
131
|
+
else
|
132
|
+
*path, key = Array(key_or_key_path)
|
133
|
+
location = path.inject(@config) do |working_object, p|
|
134
|
+
working_object[p] ||= {}
|
135
|
+
end
|
136
|
+
location[key] = value
|
137
|
+
end
|
138
|
+
|
139
|
+
invalidate_config_cache!
|
140
|
+
end
|
141
|
+
|
142
|
+
def invalidate_group_cache!
|
143
|
+
@group_cache = nil
|
144
|
+
# The config cache depends on the group cache, so invalidate it as well
|
145
|
+
invalidate_config_cache!
|
146
|
+
end
|
147
|
+
|
148
|
+
def invalidate_config_cache!
|
149
|
+
@transport = nil
|
150
|
+
@transport_config = nil
|
151
|
+
end
|
152
|
+
|
153
|
+
# Validate the target. This implicitly also primes the group and config
|
154
|
+
# caches and resolves any config references in the target's groups.
|
155
|
+
def validate
|
156
|
+
unless name.ascii_only?
|
157
|
+
raise Bolt::Inventory::ValidationError.new("Target name must be ASCII characters: #{@name}", nil)
|
158
|
+
end
|
159
|
+
|
160
|
+
if (illegal_char = @name.match(ILLEGAL_CHARS))
|
161
|
+
raise ValidationError.new("Illegal character '#{illegal_char}' in target name '#{@name}'", nil)
|
162
|
+
end
|
163
|
+
|
164
|
+
unless transport.nil? || Bolt::TRANSPORTS.include?(transport.to_sym)
|
165
|
+
raise Bolt::UnknownTransportError.new(transport, uri)
|
166
|
+
end
|
167
|
+
|
168
|
+
validate_fact_names(facts)
|
169
|
+
|
170
|
+
transport_config
|
171
|
+
end
|
172
|
+
|
173
|
+
# Validate fact names and issue a deprecation warning if any fact names have a dot.
|
174
|
+
#
|
175
|
+
private def validate_fact_names(facts)
|
176
|
+
if (dotted = facts.keys.select { |name| name.include?('.') }).any?
|
177
|
+
Bolt::Logger.deprecate(
|
178
|
+
'dotted_fact_name',
|
179
|
+
"Target '#{safe_name}' includes dotted fact names: '#{dotted.join("', '")}'. Dotted fact "\
|
180
|
+
"names are deprecated and Bolt does not automatically convert facts with dotted names to "\
|
181
|
+
"structured facts. For more information, see https://pup.pt/bolt-dotted-facts"
|
182
|
+
)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def host
|
187
|
+
@uri_obj.hostname || transport_config['host']
|
188
|
+
end
|
189
|
+
|
190
|
+
def port
|
191
|
+
@uri_obj.port || transport_config['port']
|
192
|
+
end
|
193
|
+
|
194
|
+
# For remote targets, protocol is the value of the URI scheme. For
|
195
|
+
# non-remote targets, there is no protocol.
|
196
|
+
def protocol
|
197
|
+
if remote?
|
198
|
+
@uri_obj.scheme
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# For remote targets, the transport is always 'remote'. Otherwise, it
|
203
|
+
# will be either the URI scheme or set explicitly.
|
204
|
+
def transport
|
205
|
+
if @transport.nil?
|
206
|
+
config_transport = @config['transport'] ||
|
207
|
+
group_cache.dig('config', 'transport') ||
|
208
|
+
@inventory.transport
|
209
|
+
|
210
|
+
@transport = if @uri_obj.scheme == 'remote' || config_transport == 'remote'
|
211
|
+
'remote'
|
212
|
+
else
|
213
|
+
@uri_obj.scheme || config_transport
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
@transport
|
218
|
+
end
|
219
|
+
|
220
|
+
def remote?
|
221
|
+
transport == 'remote'
|
222
|
+
end
|
223
|
+
|
224
|
+
def user
|
225
|
+
Addressable::URI.unencode_component(@uri_obj.user) || transport_config['user']
|
226
|
+
end
|
227
|
+
|
228
|
+
def password
|
229
|
+
Addressable::URI.unencode_component(@uri_obj.password) || transport_config['password']
|
230
|
+
end
|
231
|
+
|
232
|
+
# We only want to look up transport config keys for the configured
|
233
|
+
# transport
|
234
|
+
def transport_config
|
235
|
+
if @transport_config.nil?
|
236
|
+
config = @inventory.config[transport]
|
237
|
+
.merge(group_cache.dig('config', transport), @config[transport])
|
238
|
+
@transport_config = config
|
239
|
+
end
|
240
|
+
|
241
|
+
@transport_config
|
242
|
+
end
|
243
|
+
|
244
|
+
def config
|
245
|
+
Bolt::Util.deep_merge(group_cache['config'], @config)
|
246
|
+
end
|
247
|
+
|
248
|
+
def group_cache
|
249
|
+
if @group_cache.nil?
|
250
|
+
group_data = @inventory.group_data_for(@name)
|
251
|
+
|
252
|
+
unless group_data && group_data['config']
|
253
|
+
@logger.debug("Did not find config for #{self} in inventory")
|
254
|
+
end
|
255
|
+
|
256
|
+
group_data ||= {
|
257
|
+
'config' => {},
|
258
|
+
'vars' => {},
|
259
|
+
'facts' => {},
|
260
|
+
'features' => Set.new,
|
261
|
+
'plugin_hooks' => {},
|
262
|
+
'target_alias' => []
|
263
|
+
}
|
264
|
+
|
265
|
+
@group_cache = group_data
|
266
|
+
end
|
267
|
+
|
268
|
+
@group_cache
|
269
|
+
end
|
270
|
+
|
271
|
+
def to_s
|
272
|
+
@safe_name
|
273
|
+
end
|
274
|
+
|
275
|
+
def self.parse_uri(string)
|
276
|
+
require 'addressable/uri'
|
277
|
+
if string.nil?
|
278
|
+
Addressable::URI.new
|
279
|
+
# Forbid empty uri
|
280
|
+
elsif string.empty?
|
281
|
+
raise Bolt::ParseError, "Could not parse target URI: URI is empty string"
|
282
|
+
elsif string =~ %r{^[^:]+://}
|
283
|
+
Addressable::URI.parse(string)
|
284
|
+
else
|
285
|
+
# Initialize with an empty scheme to ensure we parse the hostname correctly
|
286
|
+
Addressable::URI.parse("//#{string}")
|
287
|
+
end
|
288
|
+
rescue Addressable::URI::InvalidURIError => e
|
289
|
+
raise Bolt::ParseError, "Could not parse target URI: #{e.message}"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require_relative '../bolt/config'
|
5
|
+
require_relative 'inventory/group'
|
6
|
+
require_relative 'inventory/inventory'
|
7
|
+
require_relative 'inventory/options'
|
8
|
+
require_relative '../bolt/target'
|
9
|
+
require_relative '../bolt/util'
|
10
|
+
require_relative '../bolt/plugin'
|
11
|
+
require_relative '../bolt/validator'
|
12
|
+
require 'yaml'
|
13
|
+
|
14
|
+
module Bolt
|
15
|
+
class Inventory
|
16
|
+
include Bolt::Inventory::Options
|
17
|
+
|
18
|
+
ENVIRONMENT_VAR = 'BOLT_INVENTORY'
|
19
|
+
|
20
|
+
class ValidationError < Bolt::Error
|
21
|
+
attr_accessor :path
|
22
|
+
|
23
|
+
def initialize(message, offending_group)
|
24
|
+
super(message, 'bolt.inventory/validation-error')
|
25
|
+
@_message = message
|
26
|
+
@path = [offending_group].compact
|
27
|
+
end
|
28
|
+
|
29
|
+
def details
|
30
|
+
{ 'path' => path }
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_parent(parent_group)
|
34
|
+
@path << parent_group
|
35
|
+
end
|
36
|
+
|
37
|
+
def message
|
38
|
+
if path.empty?
|
39
|
+
@_message
|
40
|
+
else
|
41
|
+
"#{@_message} for group at #{path}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class WildcardError < Bolt::Error
|
47
|
+
def initialize(target)
|
48
|
+
super("Found 0 nodes matching wildcard pattern #{target}", 'bolt.inventory/wildcard-error')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Builds the schema used by the validator.
|
53
|
+
#
|
54
|
+
def self.schema
|
55
|
+
schema = {
|
56
|
+
type: Hash,
|
57
|
+
properties: OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
|
58
|
+
definitions: DEFINITIONS,
|
59
|
+
_plugin: true
|
60
|
+
}
|
61
|
+
|
62
|
+
schema[:definitions]['config'][:properties] = Bolt::Config.transport_definitions
|
63
|
+
schema
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.from_config(config, plugins)
|
67
|
+
logger = Bolt::Logger.logger(self)
|
68
|
+
|
69
|
+
if ENV.include?(ENVIRONMENT_VAR)
|
70
|
+
begin
|
71
|
+
source = ENVIRONMENT_VAR
|
72
|
+
data = YAML.safe_load(ENV[ENVIRONMENT_VAR])
|
73
|
+
raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}" unless data.is_a?(Hash)
|
74
|
+
logger.debug("Loaded inventory from environment variable #{ENVIRONMENT_VAR}")
|
75
|
+
rescue Psych::Exception
|
76
|
+
raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}"
|
77
|
+
end
|
78
|
+
elsif config.inventoryfile
|
79
|
+
source = config.inventoryfile
|
80
|
+
data = Bolt::Util.read_yaml_hash(config.inventoryfile, 'inventory')
|
81
|
+
logger.debug("Loaded inventory from #{config.inventoryfile}")
|
82
|
+
else
|
83
|
+
source = config.default_inventoryfile
|
84
|
+
data = Bolt::Util.read_optional_yaml_hash(config.default_inventoryfile, 'inventory')
|
85
|
+
|
86
|
+
if config.default_inventoryfile.exist?
|
87
|
+
logger.debug("Loaded inventory from #{config.default_inventoryfile}")
|
88
|
+
else
|
89
|
+
source = nil
|
90
|
+
logger.debug("Tried to load inventory from #{config.default_inventoryfile}, but the file does not exist")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
Bolt::Validator.new.tap do |validator|
|
95
|
+
validator.validate(data, schema, source)
|
96
|
+
validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
|
97
|
+
end
|
98
|
+
|
99
|
+
create_version(data, config.transport, config.transports, plugins, source)
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.create_version(data, transport, transports, plugins, source = nil)
|
103
|
+
version = (data || {}).delete('version') { 2 }
|
104
|
+
|
105
|
+
case version
|
106
|
+
when 2
|
107
|
+
Bolt::Inventory::Inventory.new(data, transport, transports, plugins, source)
|
108
|
+
else
|
109
|
+
raise ValidationError.new("Unsupported version #{version} specified in inventory", nil)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.empty
|
114
|
+
config = Bolt::Config.default
|
115
|
+
plugins = Bolt::Plugin.new(config, nil)
|
116
|
+
|
117
|
+
create_version({}, config.transport, config.transports, plugins, nil)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/bolt/logger.rb
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logging'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
module Logger
|
7
|
+
LEVELS = %w[trace debug info warn error fatal].freeze
|
8
|
+
|
9
|
+
# This module is treated as a global singleton so that multiple classes
|
10
|
+
# in Bolt can log warnings with IDs. Access to the following variables
|
11
|
+
# are controlled by a mutex.
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@warnings = Set.new
|
14
|
+
@disable_warnings = Set.new
|
15
|
+
@message_queue = []
|
16
|
+
|
17
|
+
# This method provides a single point-of-entry to setup logging for both
|
18
|
+
# the CLI and for tests. This is necessary because we define custom log
|
19
|
+
# levels which create corresponding methods on the logger instances;
|
20
|
+
# without first initializing the Logging system, calls to those methods
|
21
|
+
# will fail.
|
22
|
+
def self.initialize_logging
|
23
|
+
# Initialization isn't idempotent and will result in warnings about const
|
24
|
+
# redefs, so skip it if the log levels we expect are present. If it's
|
25
|
+
# already been initialized with an insufficient set of levels, go ahead
|
26
|
+
# and call init anyway or we'll have failures when calling log methods
|
27
|
+
# for missing levels.
|
28
|
+
unless levels & LEVELS == LEVELS
|
29
|
+
Logging.init(*LEVELS)
|
30
|
+
end
|
31
|
+
|
32
|
+
# As above, only create the color scheme if we haven't already created it.
|
33
|
+
unless Logging.color_scheme('bolt')
|
34
|
+
Logging.color_scheme(
|
35
|
+
'bolt',
|
36
|
+
lines: {
|
37
|
+
warn: :yellow,
|
38
|
+
error: :red,
|
39
|
+
fatal: %i[white on_red]
|
40
|
+
}
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.configure(destinations, color, disable_warnings = nil)
|
46
|
+
root_logger = Bolt::Logger.logger(:root)
|
47
|
+
|
48
|
+
root_logger.add_appenders Logging.appenders.stderr(
|
49
|
+
'console',
|
50
|
+
layout: console_layout(color),
|
51
|
+
level: default_console_level
|
52
|
+
)
|
53
|
+
|
54
|
+
# We set the root logger's level so that it logs everything but we do
|
55
|
+
# limit what's actually logged in every appender individually.
|
56
|
+
root_logger.level = :all
|
57
|
+
|
58
|
+
destinations.each_pair do |name, params|
|
59
|
+
appender = Logging.appenders[name]
|
60
|
+
if appender.nil?
|
61
|
+
unless name.start_with?('file:')
|
62
|
+
raise Bolt::Error.new("Unexpected log: #{name}", 'bolt/internal-error')
|
63
|
+
end
|
64
|
+
|
65
|
+
begin
|
66
|
+
appender = Logging.appenders.file(
|
67
|
+
name,
|
68
|
+
filename: name[5..-1], # strip the "file:" prefix
|
69
|
+
truncate: (params[:append] == false),
|
70
|
+
layout: default_layout,
|
71
|
+
level: default_file_level
|
72
|
+
)
|
73
|
+
rescue ArgumentError => e
|
74
|
+
raise Bolt::Error.new("Failed to open log #{name}: #{e.message}", 'bolt/log-error')
|
75
|
+
end
|
76
|
+
|
77
|
+
root_logger.add_appenders appender
|
78
|
+
end
|
79
|
+
|
80
|
+
appender.level = params[:level] if params[:level]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Set the list of disabled warnings and mark the logger as configured.
|
84
|
+
# Log all messages in the message queue and flush the queue.
|
85
|
+
if disable_warnings
|
86
|
+
@mutex.synchronize { @disable_warnings = disable_warnings }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.configured?
|
91
|
+
Logging.logger[:root].appenders.any?
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.stream
|
95
|
+
@stream
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.stream=(stream)
|
99
|
+
@stream = stream
|
100
|
+
end
|
101
|
+
|
102
|
+
# A helper to ensure the Logging library is always initialized with our
|
103
|
+
# custom log levels before retrieving a Logger instance.
|
104
|
+
def self.logger(name)
|
105
|
+
initialize_logging
|
106
|
+
Logging.logger[name]
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.analytics=(analytics)
|
110
|
+
@analytics = analytics
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.console_layout(color)
|
114
|
+
color_scheme = :bolt if color
|
115
|
+
Logging.layouts.pattern(
|
116
|
+
pattern: '%m\e[0m\n',
|
117
|
+
color_scheme: color_scheme
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.default_layout
|
122
|
+
Logging.layouts.pattern(
|
123
|
+
pattern: '%d %-6l [%T] [%c] %m\n',
|
124
|
+
date_pattern: '%Y-%m-%dT%H:%M:%S.%6N'
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.default_console_level
|
129
|
+
:warn
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.default_file_level
|
133
|
+
:warn
|
134
|
+
end
|
135
|
+
|
136
|
+
# Explicitly check the log level names instead of the log level number, as levels
|
137
|
+
# that are stringified integers (e.g. "level" => "42") will return a truthy value
|
138
|
+
def self.valid_level?(level)
|
139
|
+
Logging::LEVELS.include?(Logging.levelify(level))
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.levels
|
143
|
+
Logging::LNAMES.map(&:downcase)
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.reset_logging
|
147
|
+
Logging.reset
|
148
|
+
end
|
149
|
+
|
150
|
+
# The following methods are used in place of the Logging.logger
|
151
|
+
# methods of the same name when logging warning messages or logging
|
152
|
+
# any messages prior to the logger being configured. If the logger
|
153
|
+
# is not configured when any of these methods are called, the message
|
154
|
+
# will be added to a queue, otherwise they are logged immediately.
|
155
|
+
# The message queue is flushed by calling #flush_queue, which is
|
156
|
+
# called from Bolt::CLI after configuring the logger.
|
157
|
+
#
|
158
|
+
def self.warn(id, msg)
|
159
|
+
log(type: :warn, msg: "#{msg} [ID: #{id}]", id: id)
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.warn_once(id, msg)
|
163
|
+
log(type: :warn_once, msg: "#{msg} [ID: #{id}]", id: id)
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.deprecate(id, msg)
|
167
|
+
log(type: :deprecate, msg: "#{msg} [ID: #{id}]", id: id)
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.deprecate_once(id, msg)
|
171
|
+
log(type: :deprecate_once, msg: "#{msg} [ID: #{id}]", id: id)
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.debug(msg)
|
175
|
+
log(type: :debug, msg: msg)
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.info(msg)
|
179
|
+
log(type: :info, msg: msg)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Logs a message. If the logger has not been configured, this will queue
|
183
|
+
# the message to be logged later. Once the logger is configured, the
|
184
|
+
# queue will be flushed of all messages and new messages will be logged
|
185
|
+
# immediately.
|
186
|
+
#
|
187
|
+
# Logging with this method is controlled by a mutex, as the Bolt::Logger
|
188
|
+
# module is treated as a global singleton to allow multiple classes
|
189
|
+
# access to its methods.
|
190
|
+
#
|
191
|
+
private_class_method def self.log(type:, msg:, id: nil)
|
192
|
+
@mutex.synchronize do
|
193
|
+
if configured?
|
194
|
+
log_message(type: type, msg: msg, id: id)
|
195
|
+
else
|
196
|
+
@message_queue << { type: type, msg: msg, id: id }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Logs all messages in the message queue and then flushes the queue.
|
202
|
+
#
|
203
|
+
def self.flush_queue
|
204
|
+
@mutex.synchronize do
|
205
|
+
@message_queue.each do |message|
|
206
|
+
log_message(**message)
|
207
|
+
end
|
208
|
+
|
209
|
+
@message_queue.clear
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Handles the actual logging of a message.
|
214
|
+
#
|
215
|
+
private_class_method def self.log_message(type:, msg:, id: nil)
|
216
|
+
case type
|
217
|
+
when :warn
|
218
|
+
do_warn(msg, id)
|
219
|
+
when :warn_once
|
220
|
+
do_warn_once(msg, id)
|
221
|
+
when :deprecate
|
222
|
+
do_deprecate(msg, id)
|
223
|
+
when :deprecate_once
|
224
|
+
do_deprecate_once(msg, id)
|
225
|
+
else
|
226
|
+
logger(self).send(type, msg)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# The following methods do the actual warning.
|
231
|
+
#
|
232
|
+
private_class_method def self.do_warn(msg, id)
|
233
|
+
return if @disable_warnings.include?(id)
|
234
|
+
logger(self).warn(msg)
|
235
|
+
end
|
236
|
+
|
237
|
+
private_class_method def self.do_warn_once(msg, id)
|
238
|
+
return unless @warnings.add?(id)
|
239
|
+
do_warn(msg, id)
|
240
|
+
end
|
241
|
+
|
242
|
+
private_class_method def self.do_deprecate(msg, id)
|
243
|
+
@analytics&.event('Warn', 'deprecation', label: id)
|
244
|
+
do_warn(msg, id)
|
245
|
+
end
|
246
|
+
|
247
|
+
private_class_method def self.do_deprecate_once(msg, id)
|
248
|
+
@analytics&.event('Warn', 'deprecation', label: id)
|
249
|
+
do_warn_once(msg, id)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|