bolt 0.18.2 → 0.19.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5705a6c7d44fd856ef5bf240252574ddb823a336
4
- data.tar.gz: 70027049c0bb060a5b04bd7a9ca5b00772466fc4
3
+ metadata.gz: a2227748b6143c5d3515d1c9f24faaf212eb30b7
4
+ data.tar.gz: 5547e32cf4a0d588a201c4b011bb2c9fffae983f
5
5
  SHA512:
6
- metadata.gz: 032844c9618c04162ef3bfe3e4ae6eade64f952478906d6b02539d57fd6e4325d38f35ba818e6ff1fb42fa3fd2e7f01a25527666b68560038a0c479a5ec39dfd
7
- data.tar.gz: b9df84d3b06f1d24d1c554cd5631f381356b2e0cadcbe2c2685b3b6fe610ca237611877148d5ff7f90064fc08d9925d53bfef6a0c85f0f3f5c516fc6216eb4ca
6
+ metadata.gz: 4c628c28a11c9779918352684e2ce04ab3ff078ede27694f669312d63ac9329e1538ee870bb8238efb1cc81a8b2ef1e6d44af92f550173fe16f2d274cd1d0de1
7
+ data.tar.gz: c0fdcf6f47139a80565fe393f9ad079dac585dab2cc5b490ea1db5e526be898878e0e544d0704504ca09a5ecfa9471611764f0089ea9610b2e64f2f0c49d11e5
@@ -47,7 +47,17 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
47
47
  end
48
48
 
49
49
  begin
50
- result = func.class.dispatcher.dispatchers[0].call_by_name_with_scope(scope, params, true)
50
+ # If the plan does not throw :return by calling the return function it's result is
51
+ # undef/nil
52
+ result = catch(:return) do
53
+ func.class.dispatcher.dispatchers[0].call_by_name_with_scope(scope, params, true)
54
+ nil
55
+ end&.value
56
+ # Validate the result is a PlanResult
57
+ unless Puppet::Pops::Types::TypeParser.singleton.parse('Boltlib::PlanResult').instance?(result)
58
+ raise Bolt::InvalidPlanResult.new(plan_name, result.to_s)
59
+ end
60
+ result
51
61
  rescue Puppet::PreformattedError => err
52
62
  if named_args['_catch_errors'] && err.cause.is_a?(Bolt::Error)
53
63
  result = err.cause.to_puppet_error
@@ -0,0 +1,5 @@
1
+ # A Planresult describes the supported return values of a plan. It
2
+ # should be used as the return type of functions that run plans and return the
3
+ # results.
4
+
5
+ type Boltlib::PlanResult = Variant[Boolean, Numeric, String, Undef, Error, Result, ResultSet, Target, Array[Boltlib::PlanResult], Hash[String, Boltlib::PlanResult]]
@@ -73,4 +73,13 @@ module Bolt
73
73
  new(err.msg, err.kind, err.details, err.issue_code)
74
74
  end
75
75
  end
76
+
77
+ class InvalidPlanResult < Error
78
+ def initialize(plan_name, result_str)
79
+ super("Plan #{plan_name} returned an invalid result: #{result_str}",
80
+ 'bolt/invalid-plan-result',
81
+ { 'plan_name' => plan_name,
82
+ 'result_string' => result_str })
83
+ end
84
+ end
76
85
  end
@@ -52,7 +52,6 @@ module Bolt
52
52
  if node_names.include?(@name)
53
53
  raise ValidationError.new("Group #{@name} conflicts with node of the same name", @name)
54
54
  end
55
- raise ValidationError.new("Group #{@name} is too deeply nested", @name) if depth > 1
56
55
 
57
56
  check_deprecated_config('Group', @name, @config)
58
57
 
@@ -129,7 +128,7 @@ module Bolt
129
128
  # Shallow merge instead of deep merge so that vars with a hash value
130
129
  # are assigned a new hash, rather than merging the existing value
131
130
  # with the value meant to replace it
132
- 'vars' => data2['vars'].merge(data1['vars']),
131
+ 'vars' => data1['vars'].merge(data2['vars']),
133
132
  'facts' => Bolt::Util.deep_merge(data1['facts'], data2['facts']),
134
133
  'groups' => data2['groups'] + data1['groups']
135
134
  }
@@ -163,8 +163,10 @@ module Bolt
163
163
 
164
164
  # @param [Hash] A hash representing the plan result
165
165
  def print_plan_result(result)
166
- # If the object has a json representation display it
167
- if result.respond_to?(:to_json)
166
+ if result.nil?
167
+ @stream.puts("Plan completed successfully with no result")
168
+ # Otherwise if object has a json representation display it
169
+ elsif result.respond_to?(:to_json)
168
170
  # Guard against to_json methods that don't accept options
169
171
  # and don't print empty results on multiple lines
170
172
  if result.method(:to_json).arity == 0 ||
@@ -43,12 +43,13 @@ module Bolt
43
43
  Bolt::ResultSet.include_iterable
44
44
  end
45
45
 
46
- # Create a top-level alias for TargetSpec so that users don't have to
46
+ # Create a top-level alias for TargetSpec and PlanResult so that users don't have to
47
47
  # namespace it with Boltlib, which is just an implementation detail. This
48
- # allows TargetSpec to feel like a built-in type in bolt, rather than
48
+ # allows them to feel like a built-in type in bolt, rather than
49
49
  # something has been, no pun intended, "bolted on".
50
- def add_target_spec(compiler)
50
+ def alias_types(compiler)
51
51
  compiler.evaluate_string('type TargetSpec = Boltlib::TargetSpec')
52
+ compiler.evaluate_string('type PlanResult = Boltlib::PlanResult')
52
53
  end
53
54
 
54
55
  def full_modulepath(modulepath)
@@ -61,7 +62,7 @@ module Bolt
61
62
  def in_bolt_compiler
62
63
  r = Puppet::Pal.in_tmp_environment('bolt', modulepath: full_modulepath(@config[:modulepath]), facts: {}) do |pal|
63
64
  pal.with_script_compiler do |compiler|
64
- add_target_spec(compiler)
65
+ alias_types(compiler)
65
66
  begin
66
67
  yield compiler
67
68
  rescue Puppet::PreformattedError => err
@@ -11,7 +11,9 @@ module Bolt
11
11
  module Transport
12
12
  class Orch < Base
13
13
  CONF_FILE = File.expand_path('~/.puppetlabs/client-tools/orchestrator.conf')
14
- BOLT_MOCK_TASK = Struct.new(:name, :executable).new('bolt', 'bolt/tasks/init').freeze
14
+ BOLT_COMMAND_TASK = Struct.new(:name).new('bolt_shim::command').freeze
15
+ BOLT_SCRIPT_TASK = Struct.new(:name).new('bolt_shim::script').freeze
16
+ BOLT_UPLOAD_TASK = Struct.new(:name).new('bolt_shim::upload').freeze
15
17
 
16
18
  def self.options
17
19
  %w[service-url cacert token-file task-environment local-validation]
@@ -79,11 +81,10 @@ module Bolt
79
81
 
80
82
  def batch_command(targets, command, options = {}, &callback)
81
83
  params = {
82
- action: 'command',
83
84
  command: command
84
85
  }
85
86
  results = run_task_job(targets,
86
- BOLT_MOCK_TASK,
87
+ BOLT_COMMAND_TASK,
87
88
  params,
88
89
  options,
89
90
  &callback)
@@ -98,12 +99,11 @@ module Bolt
98
99
  content = File.open(script, &:read)
99
100
  content = Base64.encode64(content)
100
101
  params = {
101
- action: 'script',
102
102
  content: content,
103
103
  arguments: arguments
104
104
  }
105
105
  callback ||= proc {}
106
- results = run_task_job(targets, BOLT_MOCK_TASK, params, options, &callback)
106
+ results = run_task_job(targets, BOLT_SCRIPT_TASK, params, options, &callback)
107
107
  results.map! { |result| unwrap_bolt_result(result.target, result) }
108
108
  results.each do |result|
109
109
  callback.call(type: :node_result, result: result)
@@ -115,13 +115,12 @@ module Bolt
115
115
  content = Base64.encode64(content)
116
116
  mode = File.stat(source).mode
117
117
  params = {
118
- action: 'upload',
119
118
  path: destination,
120
119
  content: content,
121
120
  mode: mode
122
121
  }
123
122
  callback ||= proc {}
124
- results = run_task_job(targets, BOLT_MOCK_TASK, params, options, &callback)
123
+ results = run_task_job(targets, BOLT_UPLOAD_TASK, params, options, &callback)
125
124
  results.map! do |result|
126
125
  if result.error_hash
127
126
  result
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '0.18.2'
4
+ VERSION = '0.19.0'
5
5
  end
@@ -31,5 +31,5 @@ plan aggregate::count(
31
31
  run_script($script, $nodes, $params)
32
32
  }
33
33
 
34
- aggregate::count($res)
34
+ return aggregate::count($res)
35
35
  }
@@ -31,5 +31,5 @@ plan aggregate::nodes(
31
31
  run_script($script, $nodes, $params)
32
32
  }
33
33
 
34
- aggregate::nodes($res)
34
+ return aggregate::nodes($res)
35
35
  }
@@ -48,5 +48,5 @@ plan canary(
48
48
  $restr = canary::skip($rest)
49
49
  }
50
50
 
51
- canary::merge($canr, $restr)
51
+ return canary::merge($canr, $restr)
52
52
  }
@@ -5,7 +5,7 @@
5
5
  # The $nodes parameter is a list of the nodes for which to print the OS
6
6
  # information.
7
7
  plan facts::info(TargetSpec $nodes) {
8
- run_plan(facts::retrieve, nodes => $nodes).reduce([]) |$info, $r| {
8
+ return run_plan(facts::retrieve, nodes => $nodes).reduce([]) |$info, $r| {
9
9
  if ($r.ok) {
10
10
  $info + "${r.target.name}: ${r[os][name]} ${r[os][release][full]} (${r[os][family]})"
11
11
  } else {
@@ -12,5 +12,5 @@ plan facts(TargetSpec $nodes) {
12
12
  }
13
13
  }
14
14
 
15
- $result_set
15
+ return $result_set
16
16
  }
@@ -21,7 +21,7 @@ plan facts::retrieve(TargetSpec $nodes) {
21
21
 
22
22
  # Return a single result set composed of results from the result sets
23
23
  # returned by the individual task runs.
24
- ResultSet(
24
+ return ResultSet(
25
25
  $task_targets.map |$task, $targets| {
26
26
  run_task($task, $targets, '_catch_errors' => true)
27
27
  }.reduce([]) |$results, $result_set| {
@@ -5,8 +5,11 @@ if (Get-Command facter -ErrorAction SilentlyContinue) {
5
5
  facter --json
6
6
  }
7
7
  else {
8
+ # The number 2 in the condition below is the value of
9
+ # the [System.PlatformID]::Win32NT constant. We don't
10
+ # use the constant here as it doesn't work on Windows
11
+ # Server Core.
8
12
  if ([System.Environment]::OSVersion.Platform -gt 2) {
9
- # [System.PlatformID]::Win32NT
10
13
  @'
11
14
  {
12
15
  "_error": {
@@ -24,24 +27,16 @@ else {
24
27
  $os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue
25
28
  $consumerrel = $os.ProductType -eq '1'
26
29
 
27
- if ($version -eq '10.0') {
28
- $release = if ($consumerrel) { '10' } else { '2016' }
29
- }
30
- elseif ($version -eq '6.3') {
31
- $release = if ($consumerrel) { '8.1' } else { '2012 R2' }
32
- }
33
- elseif ($version -eq '6.2') {
34
- $release = if ($consumerrel) { '8' } else { '2012' }
35
- }
36
- elseif ($version -eq '6.1') {
37
- $release = if ($consumerrel) { '7' } else { '2008 R2' }
38
- }
39
- elseif ($version -eq '6.0') {
40
- $release = if ($consumerrel) { 'Vista' } else { '2008' }
41
- }
42
- elseif ($version -eq '5.2') {
43
- $release = if ($consumerrel) { 'XP' } else {
44
- if ($os.OtherTypeDescription -eq 'R2') { '2003 R2' } else { '2003' }
30
+ $release = switch($version){
31
+ '10.0'{ if ($consumerrel) { '10' } else { '2016' } }
32
+ '6.3' { if ($consumerrel) { '8.1' } else { '2012 R2' } }
33
+ '6.2' { if ($consumerrel) { '8' } else { '2012' } }
34
+ '6.1' { if ($consumerrel) { '7' } else { '2008 R2' } }
35
+ '6.0' { if ($consumerrel) { 'Vista' } else { '2008' } }
36
+ '5.2' {
37
+ if ($consumerrel) { 'XP' } else {
38
+ if ($os.OtherTypeDescription -eq 'R2') { '2003 R2' } else { '2003' }
39
+ }
45
40
  }
46
41
  }
47
42
 
@@ -0,0 +1,22 @@
1
+ {
2
+ "description": "Manage and inspect the state of packages",
3
+ "input_method": "stdin",
4
+ "parameters": {
5
+ "action": {
6
+ "description": "The operation (install, status, uninstall and upgrade) to perform on the package",
7
+ "type": "Enum[install, status, uninstall, upgrade]"
8
+ },
9
+ "name": {
10
+ "description": "The name of the package to be manipulated",
11
+ "type": "String[1]"
12
+ },
13
+ "version": {
14
+ "description": "Version numbers must match the full version to install, including release if the provider uses a release moniker. Ranges or semver patterns are not accepted except for the gem package provider. For example, to install the bash package from the rpm bash-4.1.2-29.el6.x86_64.rpm, use the string '4.1.2-29.el6'.",
15
+ "type": "Optional[String[1]]"
16
+ },
17
+ "provider": {
18
+ "description": "The provider to use to manage or inspect the package, defaults to the system package manager",
19
+ "type": "Optional[String[1]]"
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,69 @@
1
+ #!/opt/puppetlabs/puppet/bin/ruby
2
+ require 'puppet'
3
+ # Required to find pluginsync'd plugins
4
+ Puppet.initialize_settings
5
+ require 'json'
6
+
7
+ def install(provider, _version)
8
+ if !([:absent, :purged] & Array(provider.properties[:ensure])).empty?
9
+ provider.install
10
+ provider.flush
11
+ { status: 'installed', version: Array(provider.properties[:ensure]).join(', ') }
12
+ else
13
+ { status: 'present', version: Array(provider.properties[:ensure]).join(', ') }
14
+ end
15
+ end
16
+
17
+ def status(provider, _version)
18
+ version = Array(provider.properties[:ensure])
19
+ if !([:absent, :purged] & version).empty?
20
+ { status: 'absent', version: version }
21
+ elsif provider.respond_to?(:latest)
22
+ latest = provider.latest
23
+ if !version.include?(latest)
24
+ { status: 'out of date', version: version.join(', '), latest: latest }
25
+ else
26
+ { status: 'up to date', version: version.join(', ') }
27
+ end
28
+ else
29
+ { status: 'unknown', version: version.join(', ') }
30
+ end
31
+ end
32
+
33
+ def uninstall(provider, _version)
34
+ if !([:absent, :purged] & Array(provider.properties[:ensure])).empty?
35
+ { status: 'absent' }
36
+ else
37
+ provider.uninstall
38
+ provider.flush
39
+ { status: 'uninstalled' }
40
+ end
41
+ end
42
+
43
+ def upgrade(provider, version)
44
+ old_version = Array(provider.properties[:ensure])
45
+ provider.resource[:ensure] = version unless version.nil?
46
+ provider.update
47
+ provider.flush
48
+ { old_version: old_version.join(', '), version: Array(provider.properties[:ensure]).join(', ') }
49
+ end
50
+
51
+ params = JSON.parse(STDIN.read)
52
+ name = params['name']
53
+ provider = params['provider']
54
+ action = params['action']
55
+ version = params['version']
56
+
57
+ opts = { name: name }
58
+ opts[:provider] = provider if provider
59
+
60
+ begin
61
+ provider = Puppet::Type.type(:package).new(opts).provider
62
+
63
+ result = send(action, provider, version)
64
+ puts result.to_json
65
+ exit 0
66
+ rescue Puppet::Error => e
67
+ puts({ status: 'failure', error: e.message }.to_json)
68
+ exit 1
69
+ end
@@ -0,0 +1,22 @@
1
+ {
2
+ "description": "Inspect puppet agent configuration settings",
3
+ "input_method": "stdin",
4
+ "parameters": {
5
+ "action": {
6
+ "description": "The operation (get, set) to perform on the configuration setting",
7
+ "type": "Enum[get, set]"
8
+ },
9
+ "section": {
10
+ "description": "The section of the config file. Defaults to main",
11
+ "type": "Optional[String[1]]"
12
+ },
13
+ "setting": {
14
+ "description": "The name of the config entry to set/get",
15
+ "type": "String[1]"
16
+ },
17
+ "value": {
18
+ "description": "The value you are setting. Only required for set",
19
+ "type": "Optional[String[1]]"
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,43 @@
1
+ #!/opt/puppetlabs/puppet/bin/ruby
2
+ require 'json'
3
+ require 'open3'
4
+ require 'puppet'
5
+
6
+ def set(setting, section, value)
7
+ cmd = %w[puppet config set]
8
+ cmd += ['--section', section] if section
9
+ cmd += [setting, value]
10
+ _stdout, stderr, status = Open3.capture3(*cmd)
11
+ raise Puppet::Error, stderr if status != 0
12
+ { status: value, setting: setting, section: section }
13
+ end
14
+
15
+ def get(setting, section, _value)
16
+ cmd = %w[puppet config print]
17
+ cmd += ['--section', section]
18
+ cmd += [setting]
19
+ stdout, stderr, status = Open3.capture3(*cmd)
20
+ raise Puppet::Error, stderr if status != 0
21
+ { status: stdout.strip, setting: setting, section: section }
22
+ end
23
+
24
+ params = JSON.parse(STDIN.read)
25
+ action = params['action']
26
+ setting = params['setting']
27
+ section = params['section']
28
+ value = params['value']
29
+ section = 'main' if section.nil?
30
+
31
+ begin
32
+ result = if action == 'get'
33
+ get(setting, section, value)
34
+ else
35
+ raise Puppet::Error, 'You must pass a value argument' if value.nil?
36
+ set(setting, section, value)
37
+ end
38
+ puts result.to_json
39
+ exit 0
40
+ rescue Puppet::Error => e
41
+ puts({ status: 'failure', error: e.message }.to_json)
42
+ exit 1
43
+ end
@@ -0,0 +1,18 @@
1
+ {
2
+ "description": "Manage and inspect the state of services",
3
+ "input_method": "stdin",
4
+ "parameters": {
5
+ "action": {
6
+ "description": "The operation (start, stop, restart, enable, disable, status) to perform on the service",
7
+ "type": "Enum[start, stop, restart, enable, disable, status]"
8
+ },
9
+ "name": {
10
+ "description": "The name of the service to operate on.",
11
+ "type": "String[1]"
12
+ },
13
+ "provider": {
14
+ "description": "The provider to use to manage or inspect the service, defaults to the system service manager",
15
+ "type": "Optional[String[1]]"
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,71 @@
1
+ #!/opt/puppetlabs/puppet/bin/ruby
2
+
3
+ require 'puppet'
4
+
5
+ def start(provider)
6
+ if provider.status == :running
7
+ { status: 'in_sync' }
8
+ else
9
+ provider.start
10
+ { status: 'started' }
11
+ end
12
+ end
13
+
14
+ def stop(provider)
15
+ if provider.status == :stopped
16
+ { status: 'in_sync' }
17
+ else
18
+ provider.stop
19
+ { status: 'stopped' }
20
+ end
21
+ end
22
+
23
+ def restart(provider)
24
+ provider.restart
25
+
26
+ { status: 'restarted' }
27
+ end
28
+
29
+ def status(provider)
30
+ { status: provider.status, enabled: provider.enabled? }
31
+ end
32
+
33
+ def enable(provider)
34
+ if provider.enabled?.to_s == 'true'
35
+ { status: 'in_sync' }
36
+ else
37
+ provider.enable
38
+ { status: 'enabled' }
39
+ end
40
+ end
41
+
42
+ def disable(provider)
43
+ if provider.enabled?.to_s == 'true'
44
+ provider.disable
45
+ { status: 'disabled' }
46
+ else
47
+ { status: 'in_sync' }
48
+ end
49
+ end
50
+
51
+ params = JSON.parse(STDIN.read)
52
+ name = params['name']
53
+ provider = params['provider']
54
+ action = params['action']
55
+
56
+ opts = { name: name }
57
+ opts[:provider] = provider if provider
58
+
59
+ begin
60
+ provider = Puppet::Type.type(:service).new(opts).provider
61
+
62
+ result = send(action, provider)
63
+ puts result.to_json
64
+ exit 0
65
+ rescue Puppet::Error => e
66
+ puts({ status: 'failure',
67
+ _error: { msg: e.message,
68
+ kind: 'puppet_error',
69
+ details: {} } }.to_json)
70
+ exit 1
71
+ 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.18.2
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-04 00:00:00.000000000 Z
11
+ date: 2018-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -151,25 +151,19 @@ dependencies:
151
151
  - !ruby/object:Gem::Version
152
152
  version: '2.2'
153
153
  - !ruby/object:Gem::Dependency
154
- name: gettext-setup
154
+ name: fast_gettext
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
- - - "<"
158
- - !ruby/object:Gem::Version
159
- version: '1'
160
- - - ">="
157
+ - - "~>"
161
158
  - !ruby/object:Gem::Version
162
- version: '0.10'
159
+ version: 1.1.2
163
160
  type: :runtime
164
161
  prerelease: false
165
162
  version_requirements: !ruby/object:Gem::Requirement
166
163
  requirements:
167
- - - "<"
168
- - !ruby/object:Gem::Version
169
- version: '1'
170
- - - ">="
164
+ - - "~>"
171
165
  - !ruby/object:Gem::Version
172
- version: '0.10'
166
+ version: 1.1.2
173
167
  - !ruby/object:Gem::Dependency
174
168
  name: locale
175
169
  requirement: !ruby/object:Gem::Requirement
@@ -334,6 +328,7 @@ files:
334
328
  - bolt-modules/boltlib/lib/puppet/functions/run_task.rb
335
329
  - bolt-modules/boltlib/lib/puppet/functions/set_var.rb
336
330
  - bolt-modules/boltlib/lib/puppet/functions/vars.rb
331
+ - bolt-modules/boltlib/types/planresult.pp
337
332
  - bolt-modules/boltlib/types/targetspec.pp
338
333
  - exe/bolt
339
334
  - exe/bolt-inventory-pdb
@@ -385,7 +380,13 @@ files:
385
380
  - modules/facts/tasks/bash.sh
386
381
  - modules/facts/tasks/powershell.ps1
387
382
  - modules/facts/tasks/ruby.rb
383
+ - modules/package/tasks/init.json
384
+ - modules/package/tasks/init.rb
385
+ - modules/puppet_conf/tasks/init.json
386
+ - modules/puppet_conf/tasks/init.rb
388
387
  - modules/puppetdb_fact/plans/init.pp
388
+ - modules/service/tasks/init.json
389
+ - modules/service/tasks/init.rb
389
390
  - vendored/facter/lib/facter.rb
390
391
  - vendored/facter/lib/facter/Cfkey.rb
391
392
  - vendored/facter/lib/facter/application.rb