bolt 2.8.0 → 2.12.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +2 -2
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +27 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +4 -3
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +61 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +122 -0
- data/bolt-modules/boltlib/types/planresult.pp +12 -1
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +3 -1
- data/lib/bolt/analytics.rb +21 -2
- data/lib/bolt/applicator.rb +3 -1
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/apply_target.rb +3 -2
- data/lib/bolt/bolt_option_parser.rb +18 -8
- data/lib/bolt/cli.rb +35 -5
- data/lib/bolt/config.rb +45 -13
- data/lib/bolt/config/transport/docker.rb +2 -0
- data/lib/bolt/config/transport/local.rb +2 -0
- data/lib/bolt/config/transport/orch.rb +2 -0
- data/lib/bolt/config/transport/remote.rb +2 -0
- data/lib/bolt/config/transport/ssh.rb +50 -1
- data/lib/bolt/config/transport/winrm.rb +2 -0
- data/lib/bolt/inventory.rb +2 -1
- data/lib/bolt/inventory/group.rb +1 -0
- data/lib/bolt/inventory/inventory.rb +5 -0
- data/lib/bolt/inventory/target.rb +17 -1
- data/lib/bolt/node/output.rb +1 -1
- data/lib/bolt/outputter/human.rb +5 -4
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/pal.rb +4 -1
- data/lib/bolt/pal/yaml_plan.rb +1 -0
- data/lib/bolt/plugin.rb +13 -7
- data/lib/bolt/plugin/puppetdb.rb +5 -2
- data/lib/bolt/project.rb +25 -7
- data/lib/bolt/puppetdb/config.rb +14 -26
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/resource_instance.rb +126 -0
- data/lib/bolt/result.rb +46 -23
- data/lib/bolt/result_set.rb +2 -5
- data/lib/bolt/shell/bash.rb +1 -1
- data/lib/bolt/shell/powershell.rb +12 -4
- data/lib/bolt/target.rb +12 -1
- data/lib/bolt/transport/ssh.rb +6 -2
- data/lib/bolt/transport/ssh/connection.rb +4 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +110 -0
- data/lib/bolt/transport/winrm/connection.rb +6 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/pe/pal.rb +1 -38
- data/lib/bolt_server/transport_app.rb +7 -7
- data/lib/bolt_spec/bolt_context.rb +1 -4
- data/lib/bolt_spec/plans/mock_executor.rb +1 -0
- data/lib/bolt_spec/run.rb +2 -5
- metadata +6 -2
@@ -7,6 +7,8 @@ module Bolt
|
|
7
7
|
class Config
|
8
8
|
module Transport
|
9
9
|
class WinRM < Base
|
10
|
+
# NOTE: All transport configuration options should have a corresponding schema definition
|
11
|
+
# in schemas/bolt-transport-definitions.json
|
10
12
|
OPTIONS = {
|
11
13
|
"basic-auth-only" => { type: TrueClass,
|
12
14
|
desc: "Force basic authentication. This option is only available when using SSL." },
|
data/lib/bolt/inventory.rb
CHANGED
@@ -15,6 +15,7 @@ module Bolt
|
|
15
15
|
|
16
16
|
class ValidationError < Bolt::Error
|
17
17
|
attr_accessor :path
|
18
|
+
|
18
19
|
def initialize(message, offending_group)
|
19
20
|
super(message, 'bolt.inventory/validation-error')
|
20
21
|
@_message = message
|
@@ -83,7 +84,7 @@ module Bolt
|
|
83
84
|
|
84
85
|
def self.empty
|
85
86
|
config = Bolt::Config.default
|
86
|
-
plugins = Bolt::Plugin.setup(config, nil
|
87
|
+
plugins = Bolt::Plugin.setup(config, nil)
|
87
88
|
|
88
89
|
create_version({}, config.transport, config.transports, plugins)
|
89
90
|
end
|
data/lib/bolt/inventory/group.rb
CHANGED
@@ -12,6 +12,7 @@ module Bolt
|
|
12
12
|
# Regex used to validate group names and target aliases.
|
13
13
|
NAME_REGEX = /\A[a-z0-9_][a-z0-9_-]*\Z/.freeze
|
14
14
|
|
15
|
+
# NOTE: All keys should have a corresponding schema property in schemas/bolt-inventory.schema.json
|
15
16
|
DATA_KEYS = %w[config facts vars features plugin_hooks].freeze
|
16
17
|
TARGET_KEYS = DATA_KEYS + %w[name alias uri]
|
17
18
|
GROUP_KEYS = DATA_KEYS + %w[name groups targets]
|
@@ -7,6 +7,7 @@ module Bolt
|
|
7
7
|
class Inventory
|
8
8
|
class Inventory
|
9
9
|
attr_reader :targets, :plugins, :config, :transport
|
10
|
+
|
10
11
|
class WildcardError < Bolt::Error
|
11
12
|
def initialize(target)
|
12
13
|
super("Found 0 targets matching wildcard pattern #{target}", 'bolt.inventory/wildcard-error')
|
@@ -318,6 +319,10 @@ module Bolt
|
|
318
319
|
def target_config(target)
|
319
320
|
@targets[target.name].config
|
320
321
|
end
|
322
|
+
|
323
|
+
def resources(target)
|
324
|
+
@targets[target.name].resources
|
325
|
+
end
|
321
326
|
end
|
322
327
|
end
|
323
328
|
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bolt/resource_instance'
|
4
|
+
|
3
5
|
module Bolt
|
4
6
|
class Inventory
|
5
7
|
# This class represents the active state of a target within the inventory.
|
6
8
|
class Target
|
7
|
-
attr_reader :name, :uri, :safe_name, :target_alias
|
9
|
+
attr_reader :name, :uri, :safe_name, :target_alias, :resources
|
8
10
|
|
9
11
|
def initialize(target_data, inventory)
|
10
12
|
unless target_data['name'] || target_data['uri']
|
@@ -36,12 +38,26 @@ module Bolt
|
|
36
38
|
# When alias is specified in a plan, the key will be `target_alias`, when
|
37
39
|
# alias is specified in inventory the key will be `alias`.
|
38
40
|
@target_alias = target_data['target_alias'] || target_data['alias'] || []
|
41
|
+
@resources = {}
|
39
42
|
|
40
43
|
@inventory = inventory
|
41
44
|
|
42
45
|
validate
|
43
46
|
end
|
44
47
|
|
48
|
+
# rubocop:disable Naming/AccessorMethodName
|
49
|
+
def set_resource(resource)
|
50
|
+
if (existing_resource = resources[resource.reference])
|
51
|
+
existing_resource.overwrite_state(resource.state)
|
52
|
+
existing_resource.overwrite_desired_state(resource.desired_state)
|
53
|
+
existing_resource.events = existing_resource.events + resource.events
|
54
|
+
existing_resource
|
55
|
+
else
|
56
|
+
@resources[resource.reference] = resource
|
57
|
+
end
|
58
|
+
end
|
59
|
+
# rubocop:enable Naming/AccessorMethodName
|
60
|
+
|
45
61
|
def vars
|
46
62
|
group_cache['vars'].merge(@vars)
|
47
63
|
end
|
data/lib/bolt/node/output.rb
CHANGED
data/lib/bolt/outputter/human.rb
CHANGED
@@ -107,13 +107,14 @@ module Bolt
|
|
107
107
|
|
108
108
|
# Use special handling if the result looks like a command or script result
|
109
109
|
if result.generic_value.keys == %w[stdout stderr exit_code]
|
110
|
-
|
110
|
+
safe_value = result.safe_value
|
111
|
+
unless safe_value['stdout'].strip.empty?
|
111
112
|
@stream.puts(indent(2, "STDOUT:"))
|
112
|
-
@stream.puts(indent(4,
|
113
|
+
@stream.puts(indent(4, safe_value['stdout']))
|
113
114
|
end
|
114
|
-
unless
|
115
|
+
unless safe_value['stderr'].strip.empty?
|
115
116
|
@stream.puts(indent(2, "STDERR:"))
|
116
|
-
@stream.puts(indent(4,
|
117
|
+
@stream.puts(indent(4, safe_value['stderr']))
|
117
118
|
end
|
118
119
|
elsif result.generic_value.any?
|
119
120
|
@stream.puts(indent(2, ::JSON.pretty_generate(result.generic_value)))
|
data/lib/bolt/outputter/json.rb
CHANGED
data/lib/bolt/pal.rb
CHANGED
@@ -329,7 +329,8 @@ module Bolt
|
|
329
329
|
raise Bolt::Error.unknown_plan(plan_name)
|
330
330
|
end
|
331
331
|
|
332
|
-
|
332
|
+
# path may be a Pathname object, so make sure to stringify it
|
333
|
+
mod = plan_sig.instance_variable_get(:@plan_func).loader.parent.path.to_s
|
333
334
|
|
334
335
|
# If it's a Puppet language plan, use strings to extract data. The only
|
335
336
|
# way to tell is to check which filename exists in the module.
|
@@ -358,6 +359,7 @@ module Bolt
|
|
358
359
|
name = param.name
|
359
360
|
if signature_params.include?(name)
|
360
361
|
params[name] = { 'type' => param.types.first }
|
362
|
+
params[name]['sensitive'] = param.types.first =~ /\ASensitive(\[.*\])?\z/ ? true : false
|
361
363
|
params[name]['default_value'] = defaults[name] if defaults.key?(name)
|
362
364
|
params[name]['description'] = param.text unless param.text.empty?
|
363
365
|
else
|
@@ -389,6 +391,7 @@ module Bolt
|
|
389
391
|
param.type_expr
|
390
392
|
end
|
391
393
|
params[name] = { 'type' => type_str }
|
394
|
+
params[name]['sensitive'] = param.type_expr.instance_of?(Puppet::Pops::Types::PSensitiveType)
|
392
395
|
params[name]['default_value'] = param.value
|
393
396
|
params[name]['description'] = param.description if param.description
|
394
397
|
end
|
data/lib/bolt/pal/yaml_plan.rb
CHANGED
data/lib/bolt/plugin.rb
CHANGED
@@ -119,13 +119,9 @@ module Bolt
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
def self.setup(config, pal,
|
122
|
+
def self.setup(config, pal, analytics = Bolt::Analytics::NoopClient.new)
|
123
123
|
plugins = new(config, pal, analytics)
|
124
124
|
|
125
|
-
# PDB is special because it needs the PDB client. Since it has no config,
|
126
|
-
# we can just add it first.
|
127
|
-
plugins.add_plugin(Bolt::Plugin::Puppetdb.new(pdb_client))
|
128
|
-
|
129
125
|
# Initialize any plugins referenced in plugin config. This will also indirectly
|
130
126
|
# initialize any plugins they depend on.
|
131
127
|
if plugins.reference?(config.plugins)
|
@@ -142,7 +138,7 @@ module Bolt
|
|
142
138
|
plugins
|
143
139
|
end
|
144
140
|
|
145
|
-
RUBY_PLUGINS = %w[task prompt env_var].freeze
|
141
|
+
RUBY_PLUGINS = %w[task prompt env_var puppetdb].freeze
|
146
142
|
BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory
|
147
143
|
yaml env_var gcloud_inventory].freeze
|
148
144
|
DEFAULT_PLUGIN_HOOKS = { 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }.freeze
|
@@ -161,6 +157,13 @@ module Bolt
|
|
161
157
|
@unknown = Set.new
|
162
158
|
@resolution_stack = []
|
163
159
|
@unresolved_plugin_configs = config.plugins.dup
|
160
|
+
# The puppetdb plugin config comes from the puppetdb section, not from
|
161
|
+
# the plugins section
|
162
|
+
if @unresolved_plugin_configs.key?('puppetdb')
|
163
|
+
msg = "Configuration for the PuppetDB plugin must be in the 'puppetdb' config section, not 'plugins'"
|
164
|
+
raise Bolt::Error.new(msg, 'bolt/plugin-error')
|
165
|
+
end
|
166
|
+
@unresolved_plugin_configs['puppetdb'] = config.puppetdb if config.puppetdb
|
164
167
|
@plugin_hooks = DEFAULT_PLUGIN_HOOKS.dup
|
165
168
|
end
|
166
169
|
|
@@ -168,7 +171,6 @@ module Bolt
|
|
168
171
|
@modules ||= Bolt::Module.discover(@pal.modulepath)
|
169
172
|
end
|
170
173
|
|
171
|
-
# Generally this is private. Puppetdb is special though
|
172
174
|
def add_plugin(plugin)
|
173
175
|
@plugins[plugin.name] = plugin
|
174
176
|
end
|
@@ -235,6 +237,10 @@ module Bolt
|
|
235
237
|
end
|
236
238
|
end
|
237
239
|
|
240
|
+
def puppetdb_client
|
241
|
+
by_name('puppetdb').puppetdb_client
|
242
|
+
end
|
243
|
+
|
238
244
|
# Evaluate all _plugin references in a data structure. Leaves are
|
239
245
|
# evaluated and then their parents are evaluated with references replaced
|
240
246
|
# by their values. If the result of a reference contains more references,
|
data/lib/bolt/plugin/puppetdb.rb
CHANGED
@@ -14,8 +14,11 @@ module Bolt
|
|
14
14
|
TEMPLATE_OPTS = %w[alias config facts features name uri vars].freeze
|
15
15
|
PLUGIN_OPTS = %w[_plugin query target_mapping].freeze
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
attr_reader :puppetdb_client
|
18
|
+
|
19
|
+
def initialize(config:, context:)
|
20
|
+
pdb_config = Bolt::PuppetDB::Config.load_config(config, context.boltdir)
|
21
|
+
@puppetdb_client = Bolt::PuppetDB::Client.new(pdb_config)
|
19
22
|
@logger = Logging.logger[self]
|
20
23
|
end
|
21
24
|
|
data/lib/bolt/project.rb
CHANGED
@@ -16,7 +16,18 @@ module Bolt
|
|
16
16
|
:puppetfile, :rerunfile, :type, :resource_types
|
17
17
|
|
18
18
|
def self.default_project
|
19
|
-
Project.new(File.join('~', '.puppetlabs', 'bolt'), 'user')
|
19
|
+
Project.new(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
|
20
|
+
# If homedir isn't defined use the system config path
|
21
|
+
rescue ArgumentError
|
22
|
+
Project.new(system_path, 'system')
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.system_path
|
26
|
+
if Bolt::Util.windows?
|
27
|
+
File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc')
|
28
|
+
else
|
29
|
+
File.join('/etc', 'puppetlabs', 'bolt')
|
30
|
+
end
|
20
31
|
end
|
21
32
|
|
22
33
|
# Search recursively up the directory hierarchy for the Project. Look for a
|
@@ -27,7 +38,7 @@ module Bolt
|
|
27
38
|
dir = Pathname.new(dir)
|
28
39
|
if (dir + BOLTDIR_NAME).directory?
|
29
40
|
new(dir + BOLTDIR_NAME, 'embedded')
|
30
|
-
elsif (dir + 'bolt.yaml').file?
|
41
|
+
elsif (dir + 'bolt.yaml').file? || (dir + 'bolt-project.yaml').file?
|
31
42
|
new(dir, 'local')
|
32
43
|
elsif dir.root?
|
33
44
|
default_project
|
@@ -47,7 +58,7 @@ module Bolt
|
|
47
58
|
@resource_types = @path + '.resource_types'
|
48
59
|
@type = type
|
49
60
|
|
50
|
-
@project_file = @path + 'project.yaml'
|
61
|
+
@project_file = @path + 'bolt-project.yaml'
|
51
62
|
@data = Bolt::Util.read_optional_yaml_hash(File.expand_path(@project_file), 'project') || {}
|
52
63
|
validate if load_as_module?
|
53
64
|
end
|
@@ -72,7 +83,7 @@ module Bolt
|
|
72
83
|
end
|
73
84
|
|
74
85
|
def name
|
75
|
-
# If the project is in mymod/Boltdir/project.yaml, use mymod as the project name
|
86
|
+
# If the project is in mymod/Boltdir/bolt-project.yaml, use mymod as the project name
|
76
87
|
dirname = @path.basename.to_s == 'Boltdir' ? @path.parent.basename.to_s : @path.basename.to_s
|
77
88
|
pname = @data['name'] || dirname
|
78
89
|
pname.include?('-') ? pname.split('-', 2)[1] : pname
|
@@ -100,7 +111,7 @@ module Bolt
|
|
100
111
|
n = @data['name']
|
101
112
|
if n && !project_directory_name?(n) && !project_namespaced_name?(n)
|
102
113
|
raise Bolt::ValidationError, <<~ERROR_STRING
|
103
|
-
Invalid project name '#{n}' in project.yaml; project names must match either:
|
114
|
+
Invalid project name '#{n}' in bolt-project.yaml; project names must match either:
|
104
115
|
An installed project name (ex. projectname) matching the expression /^[a-z][a-z0-9_]*$/ -or-
|
105
116
|
A namespaced project name (ex. author-projectname) matching the expression /^[a-zA-Z0-9]+[-][a-z][a-z0-9_]*$/
|
106
117
|
ERROR_STRING
|
@@ -110,7 +121,7 @@ module Bolt
|
|
110
121
|
A project name (ex. projectname) matching the expression /^[a-z][a-z0-9_]*$/ -or-
|
111
122
|
A namespaced project name (ex. author-projectname) matching the expression /^[a-zA-Z0-9]+[-][a-z][a-z0-9_]*$/
|
112
123
|
|
113
|
-
Configure project name in <project_dir>/project.yaml
|
124
|
+
Configure project name in <project_dir>/bolt-project.yaml
|
114
125
|
ERROR_STRING
|
115
126
|
# If the project name is the same as one of the built-in modules raise a warning
|
116
127
|
elsif Dir.children(Bolt::PAL::BOLTLIB_PATH).include?(name)
|
@@ -120,9 +131,16 @@ module Bolt
|
|
120
131
|
|
121
132
|
%w[tasks plans].each do |conf|
|
122
133
|
unless @data.fetch(conf, []).is_a?(Array)
|
123
|
-
raise Bolt::ValidationError, "'#{conf}' in project.yaml must be an array"
|
134
|
+
raise Bolt::ValidationError, "'#{conf}' in bolt-project.yaml must be an array"
|
124
135
|
end
|
125
136
|
end
|
126
137
|
end
|
138
|
+
|
139
|
+
def check_deprecated_file
|
140
|
+
if (@path + 'project.yaml').file?
|
141
|
+
logger = Logging.logger[self]
|
142
|
+
logger.warn "Project configuration file 'project.yaml' is deprecated; use 'bolt-project.yaml' instead."
|
143
|
+
end
|
144
|
+
end
|
127
145
|
end
|
128
146
|
end
|
data/lib/bolt/puppetdb/config.rb
CHANGED
@@ -22,27 +22,20 @@ module Bolt
|
|
22
22
|
File.expand_path(File.join(Dir::COMMON_APPDATA, 'PuppetLabs/client-tools/puppetdb.conf'))
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.load_config(
|
25
|
+
def self.load_config(options, project_path = nil)
|
26
26
|
config = {}
|
27
27
|
global_path = Bolt::Util.windows? ? default_windows_config : DEFAULT_CONFIG[:global]
|
28
|
-
if filename
|
29
|
-
if File.exist?(filename)
|
30
|
-
config = JSON.parse(File.read(filename))
|
31
|
-
else
|
32
|
-
raise Bolt::PuppetDBError, "config file #{filename} does not exist"
|
33
|
-
end
|
34
|
-
else
|
35
|
-
if File.exist?(DEFAULT_CONFIG[:user])
|
36
|
-
filepath = DEFAULT_CONFIG[:user]
|
37
|
-
elsif File.exist?(global_path)
|
38
|
-
filepath = global_path
|
39
|
-
end
|
40
28
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
29
|
+
if File.exist?(DEFAULT_CONFIG[:user])
|
30
|
+
filepath = DEFAULT_CONFIG[:user]
|
31
|
+
elsif File.exist?(global_path)
|
32
|
+
filepath = global_path
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
config = JSON.parse(File.read(filepath)) if filepath
|
37
|
+
rescue StandardError => e
|
38
|
+
Logging.logger[self].error("Could not load puppetdb.conf from #{filepath}: #{e.message}")
|
46
39
|
end
|
47
40
|
|
48
41
|
config = config.fetch('puppetdb', {})
|
@@ -51,8 +44,7 @@ module Bolt
|
|
51
44
|
|
52
45
|
def initialize(settings, project_path = nil)
|
53
46
|
@settings = settings
|
54
|
-
|
55
|
-
expand_paths
|
47
|
+
expand_paths(project_path)
|
56
48
|
end
|
57
49
|
|
58
50
|
def token
|
@@ -68,14 +60,10 @@ module Bolt
|
|
68
60
|
@token = @token.strip if @token
|
69
61
|
end
|
70
62
|
|
71
|
-
def expand_paths
|
63
|
+
def expand_paths(project_path)
|
72
64
|
%w[cacert cert key token].each do |file|
|
73
65
|
next unless @settings[file]
|
74
|
-
@settings[file] =
|
75
|
-
File.expand_path(@settings[file], @project_path)
|
76
|
-
else
|
77
|
-
File.expand_path(@settings[file])
|
78
|
-
end
|
66
|
+
@settings[file] = File.expand_path(@settings[file], project_path)
|
79
67
|
end
|
80
68
|
end
|
81
69
|
|
data/lib/bolt/rerun.rb
CHANGED
@@ -45,7 +45,7 @@ module Bolt
|
|
45
45
|
end
|
46
46
|
|
47
47
|
if result_set.is_a?(Bolt::ResultSet)
|
48
|
-
data = result_set.map { |res| res.
|
48
|
+
data = result_set.map { |res| { target: res.target.name, status: res.status } }
|
49
49
|
FileUtils.mkdir_p(File.dirname(@path))
|
50
50
|
File.write(@path, data.to_json)
|
51
51
|
elsif File.exist?(@path)
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class ResourceInstance
|
7
|
+
attr_reader :target, :type, :title, :state, :desired_state
|
8
|
+
attr_accessor :events
|
9
|
+
|
10
|
+
# Needed by Puppet to recognize Bolt::ResourceInstance as a Puppet object when deserializing
|
11
|
+
def self._pcore_type
|
12
|
+
ResourceInstance
|
13
|
+
end
|
14
|
+
|
15
|
+
# Needed by Puppet to serialize with _pcore_init_hash instead of the object's attributes
|
16
|
+
def self._pcore_init_from_hash(_init_hash)
|
17
|
+
raise "ResourceInstance shouldn't be instantiated from a pcore_init class method. "\
|
18
|
+
"How did this get called?"
|
19
|
+
end
|
20
|
+
|
21
|
+
def _pcore_init_from_hash(init_hash)
|
22
|
+
initialize(init_hash)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Parameters will already be validated when calling ResourceInstance.new or
|
26
|
+
# set_resources() from a plan. We don't perform any validation in the class
|
27
|
+
# itself since Puppet will pass an empty hash to the initializer as part of
|
28
|
+
# the deserialization process before passing the _pcore_init_hash.
|
29
|
+
def initialize(resource_hash)
|
30
|
+
@target = resource_hash['target']
|
31
|
+
@type = resource_hash['type'].to_s.capitalize
|
32
|
+
@title = resource_hash['title']
|
33
|
+
# get_resources() returns observed state under the 'parameters' key
|
34
|
+
@state = resource_hash['state'] || resource_hash['parameters'] || {}
|
35
|
+
@desired_state = resource_hash['desired_state'] || {}
|
36
|
+
@events = resource_hash['events'] || []
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates a ResourceInstance from a data hash in a plan when calling
|
40
|
+
# ResourceInstance.new($resource_hash) or $target.set_resources($resource_hash)
|
41
|
+
def self.from_asserted_hash(resource_hash)
|
42
|
+
new(resource_hash)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Creates a ResourceInstance from positional arguments in a plan when
|
46
|
+
# calling ResourceInstance.new(target, type, title, ...)
|
47
|
+
def self.from_asserted_args(target,
|
48
|
+
type,
|
49
|
+
title,
|
50
|
+
state = nil,
|
51
|
+
desired_state = nil,
|
52
|
+
events = nil)
|
53
|
+
new(
|
54
|
+
'target' => target,
|
55
|
+
'type' => type,
|
56
|
+
'title' => title,
|
57
|
+
'state' => state,
|
58
|
+
'desired_state' => desired_state,
|
59
|
+
'events' => events
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def eql?(other)
|
64
|
+
self.class.equal?(other.class) &&
|
65
|
+
target == other.target &&
|
66
|
+
type == other.type &&
|
67
|
+
title == other.title
|
68
|
+
end
|
69
|
+
alias == eql?
|
70
|
+
|
71
|
+
def to_hash
|
72
|
+
{
|
73
|
+
'target' => target,
|
74
|
+
'type' => type,
|
75
|
+
'title' => title,
|
76
|
+
'state' => state,
|
77
|
+
'desired_state' => desired_state,
|
78
|
+
'events' => events
|
79
|
+
}
|
80
|
+
end
|
81
|
+
alias _pcore_init_hash to_hash
|
82
|
+
|
83
|
+
def to_json(opts = nil)
|
84
|
+
to_hash.to_json(opts)
|
85
|
+
end
|
86
|
+
|
87
|
+
def reference
|
88
|
+
"#{type}[#{title}]"
|
89
|
+
end
|
90
|
+
alias to_s reference
|
91
|
+
|
92
|
+
def add_event(event)
|
93
|
+
@events << event
|
94
|
+
end
|
95
|
+
|
96
|
+
# rubocop:disable Naming/AccessorMethodName
|
97
|
+
def set_state(state)
|
98
|
+
assert_hash('state', state)
|
99
|
+
@state.merge!(state)
|
100
|
+
end
|
101
|
+
# rubocop:enable Naming/AccessorMethodName
|
102
|
+
|
103
|
+
def overwrite_state(state)
|
104
|
+
assert_hash('state', state)
|
105
|
+
@state = state
|
106
|
+
end
|
107
|
+
|
108
|
+
# rubocop:disable Naming/AccessorMethodName
|
109
|
+
def set_desired_state(desired_state)
|
110
|
+
assert_hash('desired_state', desired_state)
|
111
|
+
@desired_state.merge!(desired_state)
|
112
|
+
end
|
113
|
+
# rubocop:enable Naming/AccessorMethodName
|
114
|
+
|
115
|
+
def overwrite_desired_state(desired_state)
|
116
|
+
assert_hash('desired_state', desired_state)
|
117
|
+
@desired_state = desired_state
|
118
|
+
end
|
119
|
+
|
120
|
+
def assert_hash(loc, value)
|
121
|
+
unless value.is_a?(Hash)
|
122
|
+
raise Bolt::ValidationError, "#{loc} must be of type Hash; got #{value.class}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|