bolt 2.31.0 → 2.32.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 +1 -1
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
- data/guides/module.txt +19 -0
- data/guides/modulepath.txt +25 -0
- data/lib/bolt/config/options.rb +31 -13
- data/lib/bolt/module_installer.rb +70 -115
- data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
- data/lib/bolt/module_installer/puppetfile.rb +117 -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/resolver.rb +76 -0
- data/lib/bolt/module_installer/specs.rb +93 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +84 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +178 -0
- data/lib/bolt/outputter.rb +0 -47
- data/lib/bolt/outputter/human.rb +2 -2
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/project.rb +3 -8
- data/lib/bolt/project_migrator/modules.rb +10 -8
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/schemas/partials/task.json +17 -2
- metadata +21 -7
- data/lib/bolt/puppetfile.rb +0 -149
- data/lib/bolt/puppetfile/module.rb +0 -93
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
require 'bolt/error'
|
7
|
+
|
8
|
+
# This class represents a Git module specification.
|
9
|
+
#
|
10
|
+
module Bolt
|
11
|
+
class ModuleInstaller
|
12
|
+
class Specs
|
13
|
+
class GitSpec
|
14
|
+
NAME_REGEX = %r{\A(?:[a-z][a-z0-9_]*[-/])?(?<name>[a-z][a-z0-9_]*)\z}.freeze
|
15
|
+
REQUIRED_KEYS = Set.new(%w[git ref]).freeze
|
16
|
+
|
17
|
+
attr_reader :git, :ref, :type
|
18
|
+
|
19
|
+
def initialize(init_hash)
|
20
|
+
@name = parse_name(init_hash['name'])
|
21
|
+
@git, @repo = parse_git(init_hash['git'])
|
22
|
+
@ref = init_hash['ref']
|
23
|
+
@type = :git
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.implements?(hash)
|
27
|
+
REQUIRED_KEYS == hash.keys.to_set
|
28
|
+
end
|
29
|
+
|
30
|
+
# Parses the name into owner and name segments, and formats the full
|
31
|
+
# name.
|
32
|
+
#
|
33
|
+
private def parse_name(name)
|
34
|
+
return unless name
|
35
|
+
|
36
|
+
unless (match = name.match(NAME_REGEX))
|
37
|
+
raise Bolt::ValidationError,
|
38
|
+
"Invalid name for Git module specification: #{name}. Name must match "\
|
39
|
+
"'name' or 'owner/name', must start with a lowercase letter, and may "\
|
40
|
+
"only include lowercase letters, digits, and underscores."
|
41
|
+
end
|
42
|
+
|
43
|
+
match[:name]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Gets the repo from the git URL.
|
47
|
+
#
|
48
|
+
private def parse_git(git)
|
49
|
+
repo = if git.start_with?('git@github.com:')
|
50
|
+
git.split('git@github.com:').last.split('.git').first
|
51
|
+
elsif git.start_with?('https://github.com')
|
52
|
+
git.split('https://github.com/').last.split('.git').first
|
53
|
+
else
|
54
|
+
raise Bolt::ValidationError,
|
55
|
+
"Invalid git source: #{git}. Only GitHub modules are supported."
|
56
|
+
end
|
57
|
+
|
58
|
+
[git, repo]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns true if the specification is satisfied by the module.
|
62
|
+
#
|
63
|
+
def satisfied_by?(mod)
|
64
|
+
@type == mod.type && @git == mod.git
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a hash matching the module spec in bolt-project.yaml
|
68
|
+
#
|
69
|
+
def to_hash
|
70
|
+
{
|
71
|
+
'git' => @git,
|
72
|
+
'ref' => @ref
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns a PuppetfileResolver::Model::GitModule object for resolving.
|
77
|
+
#
|
78
|
+
def to_resolver_module
|
79
|
+
require 'puppetfile-resolver'
|
80
|
+
|
81
|
+
PuppetfileResolver::Puppetfile::GitModule.new(name).tap do |mod|
|
82
|
+
mod.remote = @git
|
83
|
+
mod.ref = sha
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Resolves the module's title from the module metadata. This is lazily
|
88
|
+
# resolved since Bolt does not always need to know a Git module's name.
|
89
|
+
#
|
90
|
+
def name
|
91
|
+
@name ||= begin
|
92
|
+
url = "https://raw.githubusercontent.com/#{@repo}/#{sha}/metadata.json"
|
93
|
+
response = make_request(:Get, url)
|
94
|
+
|
95
|
+
case response
|
96
|
+
when Net::HTTPOK
|
97
|
+
body = JSON.parse(response.body)
|
98
|
+
|
99
|
+
unless body.key?('name')
|
100
|
+
raise Bolt::Error.new(
|
101
|
+
"Missing name in metadata.json at #{git}. This is not a valid module.",
|
102
|
+
"bolt/missing-module-name-error"
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
parse_name(body['name'])
|
107
|
+
else
|
108
|
+
raise Bolt::Error.new(
|
109
|
+
"Missing metadata.json at #{git}. This is not a valid module.",
|
110
|
+
"bolt/missing-module-metadata-error"
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Resolves the SHA for the specified ref. This is lazily resolved since
|
117
|
+
# Bolt does not always need to know a Git module's SHA.
|
118
|
+
#
|
119
|
+
def sha
|
120
|
+
@sha ||= begin
|
121
|
+
url = "https://api.github.com/repos/#{@repo}/commits/#{ref}"
|
122
|
+
headers = ENV['GITHUB_TOKEN'] ? { "Authorization" => "token #{ENV['GITHUB_TOKEN']}" } : {}
|
123
|
+
response = make_request(:Get, url, headers)
|
124
|
+
|
125
|
+
case response
|
126
|
+
when Net::HTTPOK
|
127
|
+
body = JSON.parse(response.body)
|
128
|
+
body['sha']
|
129
|
+
when Net::HTTPUnauthorized
|
130
|
+
raise Bolt::Error.new(
|
131
|
+
"Invalid token at GITHUB_TOKEN, unable to resolve git modules.",
|
132
|
+
"bolt/invalid-git-token-error"
|
133
|
+
)
|
134
|
+
when Net::HTTPForbidden
|
135
|
+
message = "GitHub API rate limit exceeded, unable to resolve git modules. "
|
136
|
+
|
137
|
+
unless ENV['GITHUB_TOKEN']
|
138
|
+
message += "To increase your rate limit, set the GITHUB_TOKEN environment "\
|
139
|
+
"variable with a GitHub personal access token."
|
140
|
+
end
|
141
|
+
|
142
|
+
raise Bolt::Error.new(message, 'bolt/github-api-rate-limit-error')
|
143
|
+
when Net::HTTPNotFound
|
144
|
+
raise Bolt::Error.new(
|
145
|
+
"#{git} is not a git repository.",
|
146
|
+
"bolt/missing-git-repository-error"
|
147
|
+
)
|
148
|
+
else
|
149
|
+
raise Bolt::Error.new(
|
150
|
+
"Ref #{ref} at #{git} is not a commit, tag, or branch.",
|
151
|
+
"bolt/invalid-git-ref-error"
|
152
|
+
)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Makes a generic HTTP request.
|
158
|
+
#
|
159
|
+
private def make_request(verb, url, headers = {})
|
160
|
+
require 'net/http'
|
161
|
+
|
162
|
+
uri = URI.parse(url)
|
163
|
+
opts = { use_ssl: uri.scheme == 'https' }
|
164
|
+
|
165
|
+
Net::HTTP.start(uri.host, uri.port, opts) do |client|
|
166
|
+
request = Net::HTTP.const_get(verb).new(uri, headers)
|
167
|
+
client.request(request)
|
168
|
+
end
|
169
|
+
rescue StandardError => e
|
170
|
+
raise Bolt::Error.new(
|
171
|
+
"Failed to connect to #{uri}: #{e.message}",
|
172
|
+
"bolt/http-connect-error"
|
173
|
+
)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/lib/bolt/outputter.rb
CHANGED
@@ -27,10 +27,6 @@ module Bolt
|
|
27
27
|
string.gsub(/^/, indent.to_s)
|
28
28
|
end
|
29
29
|
|
30
|
-
def print_message_event(event)
|
31
|
-
print_message(stringify(event[:message]))
|
32
|
-
end
|
33
|
-
|
34
30
|
def print_message
|
35
31
|
raise NotImplementedError, "print_message() must be implemented by the outputter class"
|
36
32
|
end
|
@@ -38,49 +34,6 @@ module Bolt
|
|
38
34
|
def print_error
|
39
35
|
raise NotImplementedError, "print_error() must be implemented by the outputter class"
|
40
36
|
end
|
41
|
-
|
42
|
-
def stringify(message)
|
43
|
-
formatted = format_message(message)
|
44
|
-
if formatted.is_a?(Hash) || formatted.is_a?(Array)
|
45
|
-
::JSON.pretty_generate(formatted)
|
46
|
-
else
|
47
|
-
formatted
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def format_message(message)
|
52
|
-
case message
|
53
|
-
when Array
|
54
|
-
message.map { |item| format_message(item) }
|
55
|
-
when Bolt::ApplyResult
|
56
|
-
format_apply_result(message)
|
57
|
-
when Bolt::Result, Bolt::ResultSet
|
58
|
-
# This is equivalent to to_s, but formattable
|
59
|
-
message.to_data
|
60
|
-
when Bolt::RunFailure
|
61
|
-
formatted_resultset = message.result_set.to_data
|
62
|
-
message.to_h.merge('result_set' => formatted_resultset)
|
63
|
-
when Hash
|
64
|
-
message.each_with_object({}) do |(k, v), h|
|
65
|
-
h[format_message(k)] = format_message(v)
|
66
|
-
end
|
67
|
-
when Integer, Float, NilClass
|
68
|
-
message
|
69
|
-
else
|
70
|
-
message.to_s
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def format_apply_result(result)
|
75
|
-
logs = result.resource_logs&.map do |log|
|
76
|
-
# Omit low-level info/debug messages
|
77
|
-
next if %w[info debug].include?(log['level'])
|
78
|
-
indent(2, format_log(log))
|
79
|
-
end
|
80
|
-
hash = result.to_data
|
81
|
-
hash['logs'] = logs unless logs.empty?
|
82
|
-
hash
|
83
|
-
end
|
84
37
|
end
|
85
38
|
end
|
86
39
|
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -45,7 +45,7 @@ module Bolt
|
|
45
45
|
when :disable_default_output
|
46
46
|
@disable_depth += 1
|
47
47
|
when :message
|
48
|
-
|
48
|
+
print_message(event[:message])
|
49
49
|
end
|
50
50
|
|
51
51
|
if enabled?
|
@@ -344,7 +344,7 @@ module Bolt
|
|
344
344
|
end
|
345
345
|
|
346
346
|
@stream.puts "INVENTORY FILE:"
|
347
|
-
if
|
347
|
+
if File.exist?(inventoryfile)
|
348
348
|
@stream.puts inventoryfile
|
349
349
|
else
|
350
350
|
@stream.puts wrap("Tried to load inventory from #{inventoryfile}, but the file does not exist")
|
data/lib/bolt/outputter/json.rb
CHANGED
data/lib/bolt/project.rb
CHANGED
@@ -210,14 +210,9 @@ module Bolt
|
|
210
210
|
raise Bolt::ValidationError, "'modules' in bolt-project.yaml must be an array"
|
211
211
|
end
|
212
212
|
|
213
|
-
@data['modules'].each do |
|
214
|
-
next if
|
215
|
-
raise Bolt::ValidationError, "Module
|
216
|
-
end
|
217
|
-
|
218
|
-
unknown_keys = modules.flat_map(&:keys).uniq - %w[name version_requirement]
|
219
|
-
if unknown_keys.any?
|
220
|
-
@logs << { warn: "Ignoring unknown keys in module declarations: #{unknown_keys.join(', ')}." }
|
213
|
+
@data['modules'].each do |spec|
|
214
|
+
next if spec.is_a?(Hash) || spec.is_a?(String)
|
215
|
+
raise Bolt::ValidationError, "Module specification #{spec.inspect} must be a hash or string"
|
221
216
|
end
|
222
217
|
end
|
223
218
|
end
|
@@ -42,12 +42,14 @@ module Bolt
|
|
42
42
|
# to the new moduledir.
|
43
43
|
#
|
44
44
|
private def migrate_modules_from_puppetfile(config, puppetfile_path, managed_moduledir, modulepath)
|
45
|
-
require 'bolt/
|
46
|
-
require 'bolt/puppetfile
|
45
|
+
require 'bolt/module_installer/installer'
|
46
|
+
require 'bolt/module_installer/puppetfile'
|
47
|
+
require 'bolt/module_installer/resolver'
|
48
|
+
require 'bolt/module_installer/specs'
|
47
49
|
|
48
50
|
begin
|
49
51
|
@outputter.print_action_step("Parsing Puppetfile at #{puppetfile_path}")
|
50
|
-
puppetfile = Bolt::Puppetfile.parse(puppetfile_path, skip_unsupported_modules: true)
|
52
|
+
puppetfile = Bolt::ModuleInstaller::Puppetfile.parse(puppetfile_path, skip_unsupported_modules: true)
|
51
53
|
rescue Bolt::Error => e
|
52
54
|
@outputter.print_action_error("#{e.message}\nSkipping module migration.")
|
53
55
|
return false
|
@@ -56,14 +58,14 @@ module Bolt
|
|
56
58
|
# Prompt for direct dependencies
|
57
59
|
modules = select_modules(puppetfile.modules)
|
58
60
|
|
59
|
-
# Create
|
60
|
-
|
61
|
+
# Create specs to resolve from
|
62
|
+
specs = Bolt::ModuleInstaller::Specs.new(modules.map(&:to_hash))
|
61
63
|
|
62
64
|
# Attempt to resolve dependencies
|
63
65
|
begin
|
64
66
|
@outputter.print_message('')
|
65
67
|
@outputter.print_action_step("Resolving module dependencies, this may take a moment")
|
66
|
-
puppetfile.resolve
|
68
|
+
puppetfile = Bolt::ModuleInstaller::Resolver.new.resolve(specs)
|
67
69
|
rescue Bolt::Error => e
|
68
70
|
@outputter.print_action_error("#{e.message}\nSkipping module migration.")
|
69
71
|
return false
|
@@ -98,7 +100,7 @@ module Bolt
|
|
98
100
|
|
99
101
|
# Install Puppetfile
|
100
102
|
@outputter.print_action_step("Syncing modules from #{puppetfile_path} to #{managed_moduledir}")
|
101
|
-
Bolt::
|
103
|
+
Bolt::ModuleInstaller::Installer.new.install(puppetfile_path, managed_moduledir)
|
102
104
|
else
|
103
105
|
@outputter.print_action_step(
|
104
106
|
"Project does not include any managed modules, deleting Puppetfile "\
|
@@ -123,7 +125,7 @@ module Bolt
|
|
123
125
|
return modules if all
|
124
126
|
|
125
127
|
modules.select do |mod|
|
126
|
-
Bolt::Util.prompt_yes_no("Select #{mod.
|
128
|
+
Bolt::Util.prompt_yes_no("Select #{mod.full_name}?", @outputter)
|
127
129
|
end
|
128
130
|
end
|
129
131
|
|
data/lib/bolt/version.rb
CHANGED
@@ -63,9 +63,24 @@
|
|
63
63
|
"environment": {
|
64
64
|
"description": "Environment the task is in",
|
65
65
|
"type": "string"
|
66
|
+
},
|
67
|
+
"project": {
|
68
|
+
"description": "Project the task is in",
|
69
|
+
"type": "string"
|
66
70
|
}
|
67
71
|
},
|
68
|
-
"
|
72
|
+
"oneOf": [
|
73
|
+
{
|
74
|
+
"required": [
|
75
|
+
"environment"
|
76
|
+
]
|
77
|
+
},
|
78
|
+
{
|
79
|
+
"required": [
|
80
|
+
"project"
|
81
|
+
]
|
82
|
+
}
|
83
|
+
],
|
69
84
|
"additionalProperties": true
|
70
85
|
}
|
71
86
|
},
|
@@ -90,5 +105,5 @@
|
|
90
105
|
}
|
91
106
|
},
|
92
107
|
"required": ["name", "files"],
|
93
|
-
"additionalProperties":
|
108
|
+
"additionalProperties": true
|
94
109
|
}
|
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: 2.
|
4
|
+
version: 2.32.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -182,16 +182,22 @@ dependencies:
|
|
182
182
|
name: puppet
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
184
184
|
requirements:
|
185
|
-
- -
|
185
|
+
- - ">="
|
186
186
|
- !ruby/object:Gem::Version
|
187
187
|
version: 6.18.0
|
188
|
+
- - "<="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '6.19'
|
188
191
|
type: :runtime
|
189
192
|
prerelease: false
|
190
193
|
version_requirements: !ruby/object:Gem::Requirement
|
191
194
|
requirements:
|
192
|
-
- -
|
195
|
+
- - ">="
|
193
196
|
- !ruby/object:Gem::Version
|
194
197
|
version: 6.18.0
|
198
|
+
- - "<="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '6.19'
|
195
201
|
- !ruby/object:Gem::Dependency
|
196
202
|
name: puppetfile-resolver
|
197
203
|
requirement: !ruby/object:Gem::Requirement
|
@@ -432,6 +438,8 @@ files:
|
|
432
438
|
- bolt-modules/system/lib/puppet/functions/system/env.rb
|
433
439
|
- exe/bolt
|
434
440
|
- guides/inventory.txt
|
441
|
+
- guides/module.txt
|
442
|
+
- guides/modulepath.txt
|
435
443
|
- guides/project.txt
|
436
444
|
- lib/bolt.rb
|
437
445
|
- lib/bolt/analytics.rb
|
@@ -463,6 +471,15 @@ files:
|
|
463
471
|
- lib/bolt/logger.rb
|
464
472
|
- lib/bolt/module.rb
|
465
473
|
- lib/bolt/module_installer.rb
|
474
|
+
- lib/bolt/module_installer/installer.rb
|
475
|
+
- lib/bolt/module_installer/puppetfile.rb
|
476
|
+
- lib/bolt/module_installer/puppetfile/forge_module.rb
|
477
|
+
- lib/bolt/module_installer/puppetfile/git_module.rb
|
478
|
+
- lib/bolt/module_installer/puppetfile/module.rb
|
479
|
+
- lib/bolt/module_installer/resolver.rb
|
480
|
+
- lib/bolt/module_installer/specs.rb
|
481
|
+
- lib/bolt/module_installer/specs/forge_spec.rb
|
482
|
+
- lib/bolt/module_installer/specs/git_spec.rb
|
466
483
|
- lib/bolt/node/errors.rb
|
467
484
|
- lib/bolt/node/output.rb
|
468
485
|
- lib/bolt/outputter.rb
|
@@ -504,9 +521,6 @@ files:
|
|
504
521
|
- lib/bolt/puppetdb.rb
|
505
522
|
- lib/bolt/puppetdb/client.rb
|
506
523
|
- lib/bolt/puppetdb/config.rb
|
507
|
-
- lib/bolt/puppetfile.rb
|
508
|
-
- lib/bolt/puppetfile/installer.rb
|
509
|
-
- lib/bolt/puppetfile/module.rb
|
510
524
|
- lib/bolt/r10k_log_proxy.rb
|
511
525
|
- lib/bolt/rerun.rb
|
512
526
|
- lib/bolt/resource_instance.rb
|