bolt 0.21.0 → 0.21.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 563c0acfed40d50d62d19a1c3d8acc4ea7ff04ed
4
- data.tar.gz: 2233c30b95d4cf03e68a131cfe4e83ea527f720f
3
+ metadata.gz: 7cfabedc8ddc7e9d4f1345d676f959e08d6bf5f8
4
+ data.tar.gz: d05d6c279ff68b287402b6ea1439bec634b7caed
5
5
  SHA512:
6
- metadata.gz: 94354f851af225ecd5ecc5123fee0aca0844d947685596d605e33f81543a8c5b071e30b2bcf4e872ab188e194dbec13b69dc8850de3761b02604ef2c1f49b997
7
- data.tar.gz: 3d9e1463227a2c4ac4d0e80d6c1475e65b299f16d6371bef210f2dcbd090054a78d1590f54358fea416fdf812a7889890fb37b02ad835da81cc6d67b76046de4
6
+ metadata.gz: 959a5345daf5570d015c8a4d1decde76e963840104467c56d7cc86b1322d7a06b59cecb3d2b38c3dd8acaef59950bfce8be8f2b92b752f491646e4738b3d22e0
7
+ data.tar.gz: 3d772b7542b144c6916d04ead84a310ff3484df651a11c9332b2548b177423b634dd371263002365a8ce6d93581b01ec7b116b72678499da1fb7989e1ed19710
@@ -5,7 +5,7 @@ require 'bolt/error'
5
5
  # Deep merges a hash of facts with the existing facts on a target.
6
6
  Puppet::Functions.create_function(:add_facts) do
7
7
  # @param target A target.
8
- # @param facts A hash of fact names to values that my include structured facts.
8
+ # @param facts A hash of fact names to values that may include structured facts.
9
9
  # @return The target's new facts.
10
10
  # @example Adding facts to a target
11
11
  # add_facts($target, { 'os' => { 'family' => 'windows', 'name' => 'windows' } })
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'bolt/error'
4
4
 
5
- # Raises a Bolt::PlanFailure exception to signal to callers that the plan failed
5
+ # Raises a Bolt::PlanFailure exception to signal to callers that the plan failed.
6
6
  #
7
7
  # Plan authors should call this function when their plan is not successful. The
8
8
  # error may then be caught by another plans run_plan function or in bolt itself
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'bolt/error'
4
4
 
5
- # Runs the `plan` referenced by its name. A plan is autoloaded from <moduleroot>/plans.
5
+ # Runs the `plan` referenced by its name. A plan is autoloaded from `<moduleroot>/plans`.
6
6
  Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction) do
7
7
  # @param plan_name The plan to run.
8
8
  # @param named_args Arguments to the plan. Can also include additional options: '_catch_errors', '_run_as'.
@@ -2,8 +2,10 @@
2
2
 
3
3
  require 'bolt/error'
4
4
 
5
- # Sets a particular feature to present on a target. Features are used to determine what implementation
6
- # of a task should be run. Currently supported features are
5
+ # Sets a particular feature to present on a target.
6
+ #
7
+ # Features are used to determine what implementation of a task should be run.
8
+ # Currently supported features are
7
9
  # - powershell
8
10
  # - shell
9
11
  # - puppet-agent
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'bolt/error'
4
4
 
5
- # Returns a hash of the 'vars' (variables) assigned to a target through the
6
- # inventory file or `set_var` function.
5
+ # Returns a hash of the 'vars' (variables) assigned to a target.
7
6
  #
7
+ # Vars can be assigned through the inventory file or `set_var` function.
8
8
  # Plan authors can call this function on a target to get the variable hash
9
9
  # for that target.
10
10
  Puppet::Functions.create_function(:vars) do
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Define a block where default logging is suppressed. Messages for actions within this block
4
- # will be logged at `info` level instead of `notice`, so they will not be seen normally but
5
- # will still be present when `verbose` logging is requested.
3
+ # Define a block where default logging is suppressed.
4
+ #
5
+ # Messages for actions within this block will be logged at `info` level instead
6
+ # of `notice`, so they will not be seen normally but # will still be present
7
+ # when `verbose` logging is requested.
6
8
  Puppet::Functions.create_function(:without_default_logging) do
7
9
  # @param block The block where action logging is suppressed.
8
10
  # @return [Undef]
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bolt'
5
+ require 'bolt/catalog'
6
+ require 'json'
7
+
8
+ # This accepts a catalog request on stdin:
9
+ # { "code_ast": "JSON serialized Puppet AST",
10
+ # "code_string": "String of code, ignored if AST is provided,
11
+ # "modulepath": "Array of directories to use as the modulepath for catalog compilation.
12
+ # "target": {
13
+ # "name": "the name of the node usually fqdn fro url",
14
+ # "facts": "Hash of facts to use for the node",
15
+ # "variables": "Hash of variables to use for compilation"
16
+ # }
17
+ # }
18
+
19
+ command = ARGV[0]
20
+ if command == "parse"
21
+ code = File.open(ARGV[1], &:read)
22
+ puts JSON.pretty_generate(Bolt::Catalog.new.generate_ast(code))
23
+ elsif command == "compile"
24
+ request = if ARGV[1]
25
+ File.open(ARGV[1]) { |fh| JSON.parse(fh.read) }
26
+ else
27
+ JSON.parse(STDIN.read)
28
+ end
29
+ catalog = Bolt::Catalog.new.compile_catalog(request)
30
+ puts JSON.pretty_generate(catalog)
31
+ else
32
+ puts "USAGE: run 'bolt_catalog compile' with a request on STDIN " \
33
+ "or 'bolt_catalog parse manifest.pp' to generate JSON AST"
34
+ end
@@ -234,6 +234,9 @@ Available options are:
234
234
  define('--debug', 'Display debug logging') do |_|
235
235
  @options[:debug] = true
236
236
  end
237
+ define('--trace', 'Display error stack traces') do |_|
238
+ @options[:trace] = true
239
+ end
237
240
  define('--version', 'Display the version') do |_|
238
241
  puts Bolt::VERSION
239
242
  raise Bolt::CLIExit
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/pal'
4
+
5
+ Bolt::PAL.load_puppet
6
+
7
+ # This class exists to override evaluate_main and let us inject
8
+ # AST instead of looking for the main manifest. A better option may be to set up the
9
+ # node environment so our AST is in the '' hostclass instead of doing it here.
10
+ module Puppet
11
+ module Parser
12
+ class BoltCompiler < Puppet::Parser::Compiler
13
+ def internal_evaluator
14
+ @internal_evaluator ||= Puppet::Pops::Parser::EvaluatingParser.new
15
+ end
16
+
17
+ def dump_ast(ast)
18
+ Puppet::Pops::Serialization::ToDataConverter.convert(ast, rich_data: true, symbol_to_string: true)
19
+ end
20
+
21
+ def load_ast(ast_data)
22
+ Puppet::Pops::Serialization::FromDataConverter.convert(ast_data)
23
+ end
24
+
25
+ def parse_string(string, file = '')
26
+ internal_evaluator.parse_string(string, file)
27
+ end
28
+
29
+ def evaluate_main
30
+ main = Puppet.lookup(:pal_main)
31
+ ast = if main.is_a?(String)
32
+ parse_string(main)
33
+ else
34
+ load_ast(main)
35
+ end
36
+
37
+ bridge = Puppet::Parser::AST::PopsBridge::Program.new(ast)
38
+
39
+ # This is more or less copypaste from the super but we don't use the
40
+ # original host_class.
41
+ krt = environment.known_resource_types
42
+ @main = krt.add(Puppet::Resource::Type.new(:hostclass, '', code: bridge))
43
+ @topscope.source = @main
44
+ @main_resource = Puppet::Parser::Resource.new('class', :main, scope: @topscope, source: @main)
45
+ @topscope.resource = @main_resource
46
+ add_resource(@topscope, @main_resource)
47
+
48
+ @main_resource.evaluate
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ module Bolt
55
+ class Catalog
56
+ def in_env
57
+ Puppet::Pal.in_tmp_environment('bolt_apply', modulepath: ['~/bolt/modules/'], facts: @scope['facts']) do |pal|
58
+ yield pal
59
+ end
60
+ end
61
+
62
+ def with_puppet_settings
63
+ Dir.mktmpdir('bolt') do |dir|
64
+ cli = []
65
+ Puppet::Settings::REQUIRED_APP_SETTINGS.each do |setting|
66
+ cli << "--#{setting}" << dir
67
+ end
68
+ Puppet.settings.send(:clear_everything_for_tests)
69
+ Puppet.initialize_settings(cli)
70
+ # self.class.configure_logging
71
+ yield
72
+ end
73
+ end
74
+
75
+ def setup_node(node)
76
+ facts = Puppet.lookup(:pal_facts)
77
+ node_facts = Puppet::Node::Facts.new(Puppet[:node_name_value], facts)
78
+ node.fact_merge(node_facts)
79
+
80
+ node.parameters = node.parameters.merge(Puppet.lookup(:pal_variables))
81
+ # TODO: setup server_facts
82
+ # TODO: setup trusted in params
83
+ # TODO: setup serverversion/clientversion in params
84
+ end
85
+
86
+ def compile_node(node)
87
+ compiler = Puppet::Parser::BoltCompiler.new(node)
88
+ compiler.compile(&:to_resource)
89
+ end
90
+
91
+ def generate_ast(code)
92
+ with_puppet_settings do
93
+ Puppet::Pal.in_tmp_environment("bolt_parse") do |_pal|
94
+ node = Puppet.lookup(:pal_current_node)
95
+ compiler = Puppet::Parser::BoltCompiler.new(node)
96
+ compiler.dump_ast(compiler.parse_string(code))
97
+ end
98
+ end
99
+ end
100
+
101
+ def compile_catalog(request)
102
+ pal_main = request['code_ast'] || request['code_string']
103
+ target = request['target']
104
+ with_puppet_settings do
105
+ Puppet[:code] = ''
106
+ Puppet[:node_name_value] = target['name']
107
+ Puppet::Pal.in_tmp_environment(
108
+ 'bolt_catalog',
109
+ modulepath: request["modulepath"] || [],
110
+ facts: target["facts"] || {},
111
+ variables: target["variables"] || {}
112
+ ) do |_pal|
113
+ node = Puppet.lookup(:pal_current_node)
114
+ setup_node(node)
115
+
116
+ Puppet.override(pal_main: pal_main) do
117
+ compile_node(node)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -351,7 +351,7 @@ module Bolt
351
351
  end
352
352
 
353
353
  def outputter
354
- @outputter ||= Bolt::Outputter.for_format(config[:format], config[:color])
354
+ @outputter ||= Bolt::Outputter.for_format(config[:format], config[:color], config[:trace])
355
355
  end
356
356
  end
357
357
  end
@@ -26,6 +26,7 @@ module Bolt
26
26
  Config = Struct.new(
27
27
  :concurrency,
28
28
  :format,
29
+ :trace,
29
30
  :inventoryfile,
30
31
  :log,
31
32
  :modulepath,
@@ -197,7 +198,7 @@ module Bolt
197
198
  end
198
199
 
199
200
  def update_from_cli(options)
200
- %i[concurrency transport format modulepath inventoryfile color].each do |key|
201
+ %i[concurrency transport format trace modulepath inventoryfile color].each do |key|
201
202
  self[key] = options[key] if options.key?(key)
202
203
  end
203
204
 
@@ -138,7 +138,9 @@ module Bolt
138
138
 
139
139
  unless data
140
140
  data = {}
141
- data['config'] = { 'transport' => 'local' } if target.name == 'localhost'
141
+ unless Bolt::Util.windows?
142
+ data['config'] = { 'transport' => 'local' } if target.name == 'localhost'
143
+ end
142
144
  end
143
145
 
144
146
  unless data['config']
@@ -150,6 +152,7 @@ module Bolt
150
152
  # been instantiated
151
153
  set_vars_from_hash(target.name, data['vars']) unless @target_vars[target.name]
152
154
  set_facts(target.name, data['facts']) unless @target_facts[target.name]
155
+ data['features']&.each { |feature| set_feature(target, feature) } unless @target_features[target.name]
153
156
 
154
157
  # Use Config object to ensure config section is treated consistently with config file
155
158
  conf = @config.deep_clone
@@ -23,6 +23,7 @@ module Bolt
23
23
 
24
24
  @vars = data['vars'] || {}
25
25
  @facts = data['facts'] || {}
26
+ @features = data['features'] || []
26
27
  @config = data['config'] || {}
27
28
  @groups = if data['groups']
28
29
  data['groups'].map { |g| Group.new(g) }
@@ -89,7 +90,7 @@ module Bolt
89
90
  end
90
91
 
91
92
  # The data functions below expect and return nil or a hash of the schema
92
- # { 'config' => Hash , 'vars' => Hash, 'facts' => Hash, groups => Array }
93
+ # { 'config' => Hash , 'vars' => Hash, 'facts' => Hash, 'features' => Array, groups => Array }
93
94
  def data_for(node_name)
94
95
  data_merge(group_collect(node_name), node_collect(node_name))
95
96
  end
@@ -99,23 +100,26 @@ module Bolt
99
100
  { 'config' => data['config'] || {},
100
101
  'vars' => data['vars'] || {},
101
102
  'facts' => data['facts'] || {},
103
+ 'features' => data['features'] || [],
102
104
  # groups come from group_data
103
105
  'groups' => [] }
104
106
  end
105
107
  end
106
108
 
107
109
  def group_data
108
- { 'config' => @config,
109
- 'vars' => @vars,
110
- 'facts' => @facts,
111
- 'groups' => [@name] }
110
+ { 'config' => @config,
111
+ 'vars' => @vars,
112
+ 'facts' => @facts,
113
+ 'features' => @features,
114
+ 'groups' => [@name] }
112
115
  end
113
116
 
114
117
  def empty_data
115
- { 'config' => {},
116
- 'vars' => {},
117
- 'facts' => {},
118
- 'groups' => [] }
118
+ { 'config' => {},
119
+ 'vars' => {},
120
+ 'facts' => {},
121
+ 'features' => [],
122
+ 'groups' => [] }
119
123
  end
120
124
 
121
125
  def data_merge(data1, data2)
@@ -130,6 +134,7 @@ module Bolt
130
134
  # with the value meant to replace it
131
135
  'vars' => data1['vars'].merge(data2['vars']),
132
136
  'facts' => Bolt::Util.deep_merge(data1['facts'], data2['facts']),
137
+ 'features' => data1['features'] | data2['features'],
133
138
  'groups' => data2['groups'] + data1['groups']
134
139
  }
135
140
  end
@@ -2,19 +2,20 @@
2
2
 
3
3
  module Bolt
4
4
  class Outputter
5
- def self.for_format(format, color)
5
+ def self.for_format(format, color, trace)
6
6
  case format
7
7
  when 'human'
8
- Bolt::Outputter::Human.new(color)
8
+ Bolt::Outputter::Human.new(color, trace)
9
9
  when 'json'
10
- Bolt::Outputter::JSON.new(color)
10
+ Bolt::Outputter::JSON.new(color, trace)
11
11
  when nil
12
12
  raise "Cannot use outputter before parsing."
13
13
  end
14
14
  end
15
15
 
16
- def initialize(color, stream = $stdout)
16
+ def initialize(color, trace, stream = $stdout)
17
17
  @color = color
18
+ @trace = trace
18
19
  @stream = stream
19
20
  end
20
21
 
@@ -175,6 +175,12 @@ module Bolt
175
175
  if err.is_a? Bolt::RunFailure
176
176
  @stream.puts ::JSON.pretty_generate(err.result_set)
177
177
  end
178
+
179
+ if @trace && err.backtrace
180
+ err.backtrace.each do |line|
181
+ @stream.puts(colorize(:red, "\t#{line}"))
182
+ end
183
+ end
178
184
  end
179
185
  end
180
186
 
@@ -3,11 +3,11 @@
3
3
  module Bolt
4
4
  class Outputter
5
5
  class JSON < Bolt::Outputter
6
- def initialize(color, stream = $stdout)
6
+ def initialize(color, trace, stream = $stdout)
7
7
  @items_open = false
8
8
  @object_open = false
9
9
  @preceding_item = false
10
- super(color, stream)
10
+ super(color, trace, stream)
11
11
  end
12
12
 
13
13
  def print_head
@@ -60,7 +60,12 @@ module Bolt
60
60
  def fatal_error(err)
61
61
  @stream.puts "],\n" if @items_open
62
62
  @stream.puts '"_error": ' if @object_open
63
- @stream.puts err.to_json
63
+ err_obj = err.to_h
64
+ if @trace && err.backtrace
65
+ err_obj[:details] ||= {}
66
+ err_obj[:details][:backtrace] = err.backtrace
67
+ end
68
+ @stream.puts err_obj.to_json
64
69
  @stream.puts '}' if @object_open
65
70
  end
66
71
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '0.21.0'
4
+ VERSION = '0.21.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.21.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-19 00:00:00.000000000 Z
11
+ date: 2018-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -324,6 +324,7 @@ email:
324
324
  executables:
325
325
  - bolt
326
326
  - bolt-inventory-pdb
327
+ - bolt_catalog
327
328
  extensions: []
328
329
  extra_rdoc_files: []
329
330
  files:
@@ -349,9 +350,11 @@ files:
349
350
  - bolt-modules/boltlib/types/targetspec.pp
350
351
  - exe/bolt
351
352
  - exe/bolt-inventory-pdb
353
+ - exe/bolt_catalog
352
354
  - lib/bolt.rb
353
355
  - lib/bolt/analytics.rb
354
356
  - lib/bolt/bolt_option_parser.rb
357
+ - lib/bolt/catalog.rb
355
358
  - lib/bolt/cli.rb
356
359
  - lib/bolt/config.rb
357
360
  - lib/bolt/error.rb