r10k 3.3.0 → 3.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -11
- data/CHANGELOG.mkd +68 -35
- data/CODEOWNERS +1 -0
- data/README.mkd +0 -6
- data/azure-pipelines.yml +58 -58
- data/doc/dynamic-environments/usage.mkd +12 -0
- data/doc/faq.mkd +6 -1
- data/docker/Gemfile +4 -14
- data/docker/Makefile +6 -7
- data/docker/r10k/Dockerfile +15 -5
- data/docker/{r10k/spec → spec}/dockerfile_spec.rb +1 -10
- data/docker/{r10k/spec → spec}/fixtures/Puppetfile +0 -0
- data/lib/r10k/action/deploy/environment.rb +20 -2
- data/lib/r10k/cli/deploy.rb +1 -0
- data/lib/r10k/forge/module_release.rb +51 -33
- data/lib/r10k/module/git.rb +5 -0
- data/lib/r10k/puppetfile.rb +28 -5
- data/lib/r10k/util/platform.rb +12 -0
- data/lib/r10k/version.rb +1 -1
- data/locales/r10k.pot +10 -6
- data/r10k.gemspec +1 -1
- data/spec/fixtures/unit/puppetfile/default-branch-override/Puppetfile +5 -0
- data/spec/unit/action/deploy/environment_spec.rb +63 -0
- data/spec/unit/forge/module_release_spec.rb +63 -27
- data/spec/unit/puppetfile_spec.rb +19 -0
- metadata +8 -8
- data/docker/ci/build.ps1 +0 -108
- data/integration/tests/basic_functionality/negative/attempt_to_install_peonly_module_without_license.rb +0 -71
data/docker/Makefile
CHANGED
@@ -4,18 +4,18 @@ git_describe = $(shell git describe)
|
|
4
4
|
vcs_ref := $(shell git rev-parse HEAD)
|
5
5
|
build_date := $(shell date -u +%FT%T)
|
6
6
|
hadolint_available := $(shell hadolint --help > /dev/null 2>&1; echo $$?)
|
7
|
-
hadolint_command := hadolint --ignore DL3008 --ignore DL3018 --ignore DL4000 --ignore DL4001
|
7
|
+
hadolint_command := hadolint --ignore DL3008 --ignore DL3018 --ignore DL3028 --ignore DL4000 --ignore DL4001
|
8
8
|
hadolint_container := hadolint/hadolint:latest
|
9
9
|
pwd := $(shell pwd)
|
10
10
|
export BUNDLE_PATH = $(pwd)/.bundle/gems
|
11
11
|
export BUNDLE_BIN = $(pwd)/.bundle/bin
|
12
12
|
export GEMFILE = $(pwd)/Gemfile
|
13
13
|
|
14
|
-
version
|
14
|
+
version := $(shell command grep VERSION ../lib/r10k/version.rb | awk '{print $$3}' | sed "s/'//g")
|
15
15
|
dockerfile := Dockerfile
|
16
16
|
|
17
17
|
prep:
|
18
|
-
@git fetch --unshallow ||:
|
18
|
+
@git fetch --unshallow 2> /dev/null ||:
|
19
19
|
@git fetch origin 'refs/tags/*:refs/tags/*'
|
20
20
|
|
21
21
|
lint:
|
@@ -34,8 +34,7 @@ build: prep
|
|
34
34
|
--build-arg version=$(version) \
|
35
35
|
--build-arg pupperware_analytics_stream=$(PUPPERWARE_ANALYTICS_STREAM) \
|
36
36
|
--file r10k/$(dockerfile) \
|
37
|
-
--tag $(NAMESPACE)/r10k:$(version)
|
38
|
-
r10k
|
37
|
+
--tag $(NAMESPACE)/r10k:$(version) $(pwd)/..
|
39
38
|
ifeq ($(IS_LATEST),true)
|
40
39
|
@docker tag $(NAMESPACE)/r10k:$(version) puppet/r10k:latest
|
41
40
|
endif
|
@@ -44,7 +43,7 @@ test: prep
|
|
44
43
|
@bundle install --path $$BUNDLE_PATH --gemfile $$GEMFILE
|
45
44
|
@PUPPET_TEST_DOCKER_IMAGE=$(NAMESPACE)/r10k:$(version) \
|
46
45
|
bundle exec --gemfile $$GEMFILE \
|
47
|
-
rspec
|
46
|
+
rspec spec
|
48
47
|
|
49
48
|
push-image: prep
|
50
49
|
@docker push $(NAMESPACE)/r10k:$(version)
|
@@ -64,4 +63,4 @@ push-readme:
|
|
64
63
|
|
65
64
|
publish: push-image push-readme
|
66
65
|
|
67
|
-
.PHONY:
|
66
|
+
.PHONY: lint build test prep publish push-image push-readme
|
data/docker/r10k/Dockerfile
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
FROM alpine:3.9 as build
|
2
|
+
|
3
|
+
RUN apk add --no-cache ruby git
|
4
|
+
RUN mkdir /workspace
|
5
|
+
WORKDIR /workspace
|
6
|
+
COPY . /workspace
|
7
|
+
RUN gem build r10k.gemspec && \
|
8
|
+
mv r10k*.gem r10k.gem
|
9
|
+
|
1
10
|
FROM alpine:3.9
|
2
11
|
|
3
12
|
ARG vcs_ref
|
@@ -23,14 +32,15 @@ LABEL org.label-schema.maintainer="Puppet Release Team <release@puppet.com>" \
|
|
23
32
|
org.label-schema.dockerfile="/Dockerfile"
|
24
33
|
|
25
34
|
RUN apk add --no-cache ruby openssh-client git ruby-rugged curl ruby-dev make gcc musl-dev
|
35
|
+
COPY --from=build /workspace/r10k.gem /
|
36
|
+
RUN gem install --no-doc r10k.gem json etc && \
|
37
|
+
rm -f r10k.gem
|
26
38
|
|
27
|
-
|
28
|
-
|
29
|
-
COPY docker-entrypoint.sh /
|
39
|
+
COPY docker/r10k/docker-entrypoint.sh /
|
30
40
|
RUN chmod +x /docker-entrypoint.sh
|
31
|
-
COPY docker-entrypoint.d /docker-entrypoint.d
|
41
|
+
COPY docker/r10k/docker-entrypoint.d /docker-entrypoint.d
|
32
42
|
|
33
43
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
34
44
|
CMD ["help"]
|
35
45
|
|
36
|
-
COPY Dockerfile /
|
46
|
+
COPY docker/r10k/Dockerfile /
|
@@ -14,16 +14,7 @@ describe 'r10k container' do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
before(:all) do
|
17
|
-
@image =
|
18
|
-
if @image.nil?
|
19
|
-
error_message = <<-MSG
|
20
|
-
* * * * *
|
21
|
-
PUPPET_TEST_DOCKER_IMAGE environment variable must be set so we
|
22
|
-
know which image to test against!
|
23
|
-
* * * * *
|
24
|
-
MSG
|
25
|
-
fail error_message
|
26
|
-
end
|
17
|
+
@image = require_test_image
|
27
18
|
end
|
28
19
|
|
29
20
|
after(:all) do
|
File without changes
|
@@ -129,7 +129,7 @@ module R10K
|
|
129
129
|
end
|
130
130
|
|
131
131
|
def visit_puppetfile(puppetfile)
|
132
|
-
puppetfile.load
|
132
|
+
puppetfile.load(@opts[:'default-branch-override'])
|
133
133
|
|
134
134
|
yield
|
135
135
|
|
@@ -145,11 +145,24 @@ module R10K
|
|
145
145
|
end
|
146
146
|
|
147
147
|
def write_environment_info!(environment, started_at, success)
|
148
|
+
module_deploys = []
|
149
|
+
begin
|
150
|
+
environment.puppetfile.modules.each do |mod|
|
151
|
+
name = mod.name
|
152
|
+
version = mod.version
|
153
|
+
sha = mod.repo.head rescue nil
|
154
|
+
module_deploys.push({:name => name, :version => version, :sha => sha})
|
155
|
+
end
|
156
|
+
rescue
|
157
|
+
logger.debug("Unable to get environment module deploy data for .r10k-deploy.json at #{environment.path}")
|
158
|
+
end
|
159
|
+
|
148
160
|
File.open("#{environment.path}/.r10k-deploy.json", 'w') do |f|
|
149
161
|
deploy_info = environment.info.merge({
|
150
162
|
:started_at => started_at,
|
151
163
|
:finished_at => Time.new,
|
152
164
|
:deploy_success => success,
|
165
|
+
:module_deploys => module_deploys,
|
153
166
|
})
|
154
167
|
|
155
168
|
f.puts(JSON.pretty_generate(deploy_info))
|
@@ -166,7 +179,12 @@ module R10K
|
|
166
179
|
end
|
167
180
|
|
168
181
|
def allowed_initialize_opts
|
169
|
-
super.merge(puppetfile: :self,
|
182
|
+
super.merge(puppetfile: :self,
|
183
|
+
cachedir: :self,
|
184
|
+
'no-force': :self,
|
185
|
+
'generate-types': :self,
|
186
|
+
'puppet-path': :self,
|
187
|
+
'default-branch-override': :self)
|
170
188
|
end
|
171
189
|
end
|
172
190
|
end
|
data/lib/r10k/cli/deploy.rb
CHANGED
@@ -61,6 +61,7 @@ scheduled. On subsequent deployments, Puppetfile deployment will default to off.
|
|
61
61
|
DESCRIPTION
|
62
62
|
|
63
63
|
flag :p, :puppetfile, 'Deploy modules from a puppetfile'
|
64
|
+
required nil, :'default-branch-override', 'Specify a branchname to override the default branch in the puppetfile'
|
64
65
|
|
65
66
|
runner R10K::Action::CriRunner.wrap(R10K::Action::Deploy::Environment)
|
66
67
|
end
|
@@ -40,6 +40,10 @@ module R10K
|
|
40
40
|
# @return [Pathname] Where the md5 of the cached tarball is stored.
|
41
41
|
attr_accessor :md5_file_path
|
42
42
|
|
43
|
+
# @!attribute [rw] sha256_file_path
|
44
|
+
# @return [Pathname] Where the SHA256 of the cached tarball is stored.
|
45
|
+
attr_accessor :sha256_file_path
|
46
|
+
|
43
47
|
# @!attribute [rw] unpack_path
|
44
48
|
# @return [Pathname] Where the module will be unpacked to.
|
45
49
|
attr_accessor :unpack_path
|
@@ -65,6 +69,9 @@ module R10K
|
|
65
69
|
md5_filename = @forge_release.slug + '.md5'
|
66
70
|
@md5_file_path = @tarball_cache_root + md5_filename
|
67
71
|
|
72
|
+
sha256_filename = @forge_release.slug + '.sha256'
|
73
|
+
@sha256_file_path = @tarball_cache_root + sha256_filename
|
74
|
+
|
68
75
|
@unpack_path = Pathname.new(Dir.mktmpdir) + @forge_release.slug
|
69
76
|
end
|
70
77
|
|
@@ -104,54 +111,79 @@ module R10K
|
|
104
111
|
# module release checksum given by the Puppet Forge. On mismatch, remove
|
105
112
|
# the cached copy.
|
106
113
|
#
|
114
|
+
# @raise [R10K::Error] when no SHA256 is available and FIPS mode is on
|
107
115
|
# @return [void]
|
108
116
|
def verify
|
109
117
|
logger.debug1 "Verifying that #{@tarball_cache_path} matches checksum"
|
110
118
|
|
111
|
-
|
119
|
+
sha256_of_tarball = Digest::SHA256.file(@tarball_cache_path).hexdigest
|
112
120
|
|
113
|
-
if @
|
114
|
-
|
121
|
+
if @sha256_file_path.exist?
|
122
|
+
verify_from_file(sha256_of_tarball, @sha256_file_path)
|
115
123
|
else
|
116
|
-
|
124
|
+
if @forge_release.respond_to?(:file_sha256) && !@forge_release.file_sha256.nil? && !@forge_release.file_sha256.size.zero?
|
125
|
+
forge_256_checksum = @forge_release.file_sha256
|
126
|
+
verify_from_forge(sha256_of_tarball, forge_256_checksum, @sha256_file_path)
|
127
|
+
else
|
128
|
+
if R10K::Util::Platform.fips?
|
129
|
+
raise R10K::Error, "Could not verify module, no SHA256 checksum available, and MD5 checksums not allowed in FIPS mode"
|
130
|
+
end
|
131
|
+
|
132
|
+
logger.debug1 "No SHA256 checksum available, falling back to MD5"
|
133
|
+
md5_of_tarball = Digest::MD5.file(@tarball_cache_path).hexdigest
|
134
|
+
if @md5_file_path.exist?
|
135
|
+
verify_from_file(md5_of_tarball, @md5_file_path)
|
136
|
+
else
|
137
|
+
verify_from_forge(md5_of_tarball, @forge_release.file_md5, @md5_file_path)
|
138
|
+
end
|
139
|
+
end
|
117
140
|
end
|
118
141
|
end
|
119
142
|
|
120
|
-
# Verify the
|
143
|
+
# Verify the checksum of the cached tarball against the
|
121
144
|
# module release checksum stored in the cache as well.
|
122
145
|
# On mismatch, remove the cached copy of both files.
|
146
|
+
# @param tarball_checksum [String] the checksum (either md5 or SHA256)
|
147
|
+
# of the downloaded module tarball
|
148
|
+
# @param file [Pathname] the file containing the checksum as downloaded
|
149
|
+
# previously from the forge
|
150
|
+
# @param digest_class [Digest::SHA256, Digest::MD5] which checksum type
|
151
|
+
# to verify with
|
123
152
|
#
|
124
153
|
# @raise [PuppetForge::V3::Release::ChecksumMismatch] The
|
125
154
|
# cached module release checksum doesn't match the cached checksum.
|
126
155
|
#
|
127
156
|
# @return [void]
|
128
|
-
def
|
129
|
-
|
130
|
-
if
|
131
|
-
logger.error "
|
132
|
-
|
133
|
-
|
157
|
+
def verify_from_file(tarball_checksum, checksum_file_path)
|
158
|
+
checksum_from_file = File.read(checksum_file_path).strip
|
159
|
+
if tarball_checksum != checksum_from_file
|
160
|
+
logger.error "Checksum of #{@tarball_cache_path} (#{tarball_checksum}) does not match checksum #{checksum_from_file} in #{checksum_file_path}. Removing both files."
|
161
|
+
@tarball_cache_path.delete
|
162
|
+
checksum_file_path.delete
|
134
163
|
raise PuppetForge::V3::Release::ChecksumMismatch.new
|
135
164
|
end
|
136
165
|
end
|
137
166
|
|
138
|
-
# Verify the
|
167
|
+
# Verify the checksum of the cached tarball against the
|
139
168
|
# module release checksum from the forge.
|
140
169
|
# On mismatch, remove the cached copy of the tarball.
|
170
|
+
# @param tarball_checksum [String] the checksum (either md5 or SHA256)
|
171
|
+
# of the downloaded module tarball
|
172
|
+
# @param forge_checksum [String] the checksum downloaded from the Forge
|
173
|
+
# @param checksum_file_path [Pathname] the path to write the verified
|
174
|
+
# checksum to
|
141
175
|
#
|
142
176
|
# @raise [PuppetForge::V3::Release::ChecksumMismatch] The
|
143
177
|
# cached module release checksum doesn't match the forge checksum.
|
144
178
|
#
|
145
179
|
# @return [void]
|
146
|
-
def verify_from_forge(
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
logger.debug1 "MD5 of #{@tarball_cache_path} (#{md5_of_tarball}) does not match checksum #{md5_from_forge} found on the forge. Removing tarball."
|
151
|
-
cleanup_cached_tarball_path
|
180
|
+
def verify_from_forge(tarball_checksum, forge_checksum, checksum_file_path)
|
181
|
+
if tarball_checksum != forge_checksum
|
182
|
+
logger.debug1 "Checksum of #{@tarball_cache_path} (#{tarball_checksum}) does not match checksum #{forge_checksum} found on the forge. Removing tarball."
|
183
|
+
@tarball_cache_path.delete
|
152
184
|
raise PuppetForge::V3::Release::ChecksumMismatch.new
|
153
185
|
else
|
154
|
-
File.write(
|
186
|
+
File.write(checksum_file_path, forge_checksum)
|
155
187
|
end
|
156
188
|
end
|
157
189
|
|
@@ -191,20 +223,6 @@ module R10K
|
|
191
223
|
download_path.parent.rmtree
|
192
224
|
end
|
193
225
|
end
|
194
|
-
|
195
|
-
# Remove the cached module release.
|
196
|
-
def cleanup_cached_tarball_path
|
197
|
-
if tarball_cache_path.exist?
|
198
|
-
tarball_cache_path.delete
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
# Remove the module release md5.
|
203
|
-
def cleanup_md5_file_path
|
204
|
-
if md5_file_path.exist?
|
205
|
-
md5_file_path.delete
|
206
|
-
end
|
207
|
-
end
|
208
226
|
end
|
209
227
|
end
|
210
228
|
end
|
data/lib/r10k/module/git.rb
CHANGED
@@ -23,6 +23,11 @@ class R10K::Module::Git < R10K::Module::Base
|
|
23
23
|
# @return [String]
|
24
24
|
attr_reader :desired_ref
|
25
25
|
|
26
|
+
# @!attribute [r] default_ref
|
27
|
+
# @api private
|
28
|
+
# @return [String]
|
29
|
+
attr_reader :default_ref
|
30
|
+
|
26
31
|
def initialize(title, dirname, args, environment=nil)
|
27
32
|
super
|
28
33
|
|
data/lib/r10k/puppetfile.rb
CHANGED
@@ -63,17 +63,20 @@ class Puppetfile
|
|
63
63
|
@loaded = false
|
64
64
|
end
|
65
65
|
|
66
|
-
def load
|
66
|
+
def load(default_branch_override = nil)
|
67
67
|
if File.readable? @puppetfile_path
|
68
|
-
self.load!
|
68
|
+
self.load!(default_branch_override)
|
69
69
|
else
|
70
70
|
logger.debug _("Puppetfile %{path} missing or unreadable") % {path: @puppetfile_path.inspect}
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
def load!
|
74
|
+
def load!(default_branch_override = nil)
|
75
|
+
@default_branch_override = default_branch_override
|
76
|
+
|
75
77
|
dsl = R10K::Puppetfile::DSL.new(self)
|
76
78
|
dsl.instance_eval(puppetfile_contents, @puppetfile_path)
|
79
|
+
|
77
80
|
validate_no_duplicate_names(@modules)
|
78
81
|
@loaded = true
|
79
82
|
rescue SyntaxError, LoadError, ArgumentError, NameError => e
|
@@ -118,6 +121,10 @@ class Puppetfile
|
|
118
121
|
install_path = @moduledir
|
119
122
|
end
|
120
123
|
|
124
|
+
if args.is_a?(Hash) && @default_branch_override != nil
|
125
|
+
args[:default_branch] = @default_branch_override
|
126
|
+
end
|
127
|
+
|
121
128
|
# Keep track of all the content this Puppetfile is managing to enable purging.
|
122
129
|
@managed_content[install_path] = Array.new unless @managed_content.has_key?(install_path)
|
123
130
|
|
@@ -179,7 +186,21 @@ class Puppetfile
|
|
179
186
|
logger.debug _("Updating modules with %{pool_size} threads") % {pool_size: pool_size}
|
180
187
|
mods_queue = modules_queue(visitor)
|
181
188
|
thread_pool = pool_size.times.map { visitor_thread(visitor, mods_queue) }
|
182
|
-
|
189
|
+
thread_exception = nil
|
190
|
+
|
191
|
+
# If any threads raise an exception the deployment is considered a failure.
|
192
|
+
# In that event clear the queue, wait for other threads to finish their
|
193
|
+
# current work, then re-raise the first exception caught.
|
194
|
+
begin
|
195
|
+
thread_pool.each(&:join)
|
196
|
+
rescue => e
|
197
|
+
logger.error _("Error during concurrent deploy of a module: %{message}") % {message: e.message}
|
198
|
+
mods_queue.clear
|
199
|
+
thread_exception ||= e
|
200
|
+
retry
|
201
|
+
ensure
|
202
|
+
raise thread_exception unless thread_exception.nil?
|
203
|
+
end
|
183
204
|
end
|
184
205
|
|
185
206
|
def modules_queue(visitor)
|
@@ -195,8 +216,10 @@ class Puppetfile
|
|
195
216
|
begin
|
196
217
|
while mod = mods_queue.pop(true) do mod.accept(visitor) end
|
197
218
|
rescue ThreadError => e
|
198
|
-
logger.
|
219
|
+
logger.debug _("Module thread %{id} exiting: %{message}") % {message: e.message, id: Thread.current.object_id}
|
199
220
|
Thread.exit
|
221
|
+
rescue => e
|
222
|
+
Thread.main.raise(e)
|
200
223
|
end
|
201
224
|
end
|
202
225
|
end
|
data/lib/r10k/util/platform.rb
CHANGED
@@ -3,6 +3,8 @@ require 'rbconfig'
|
|
3
3
|
module R10K
|
4
4
|
module Util
|
5
5
|
module Platform
|
6
|
+
FIPS_FILE = "/proc/sys/crypto/fips_enabled"
|
7
|
+
|
6
8
|
def self.platform
|
7
9
|
# Test JRuby first to handle JRuby on Windows as well.
|
8
10
|
if self.jruby?
|
@@ -14,6 +16,16 @@ module R10K
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
# We currently only suport FIPS mode on redhat 7, where it is
|
20
|
+
# toggled via a file.
|
21
|
+
def self.fips?
|
22
|
+
if File.exist?(FIPS_FILE)
|
23
|
+
File.read(FIPS_FILE).chomp == "1"
|
24
|
+
else
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
17
29
|
def self.windows?
|
18
30
|
RbConfig::CONFIG['host_os'] =~ /mswin|win32|dos|mingw|cygwin/i
|
19
31
|
end
|
data/lib/r10k/version.rb
CHANGED
data/locales/r10k.pot
CHANGED
@@ -6,11 +6,11 @@
|
|
6
6
|
#, fuzzy
|
7
7
|
msgid ""
|
8
8
|
msgstr ""
|
9
|
-
"Project-Id-Version: r10k 3.
|
9
|
+
"Project-Id-Version: r10k 3.3.0-7-g6a2159a\n"
|
10
10
|
"\n"
|
11
11
|
"Report-Msgid-Bugs-To: docs@puppetlabs.com\n"
|
12
|
-
"POT-Creation-Date: 2019-
|
13
|
-
"PO-Revision-Date: 2019-
|
12
|
+
"POT-Creation-Date: 2019-06-14 22:40+0000\n"
|
13
|
+
"PO-Revision-Date: 2019-06-14 22:40+0000\n"
|
14
14
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
15
15
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
16
16
|
"Language: \n"
|
@@ -343,11 +343,15 @@ msgstr ""
|
|
343
343
|
msgid "Updating modules with %{pool_size} threads"
|
344
344
|
msgstr ""
|
345
345
|
|
346
|
-
#: ../lib/r10k/puppetfile.rb:
|
347
|
-
msgid "
|
346
|
+
#: ../lib/r10k/puppetfile.rb:190
|
347
|
+
msgid "Error during concurrent deploy of a module: %{message}"
|
348
348
|
msgstr ""
|
349
349
|
|
350
|
-
#: ../lib/r10k/puppetfile.rb:
|
350
|
+
#: ../lib/r10k/puppetfile.rb:212
|
351
|
+
msgid "Module thread %{id} exiting: %{message}"
|
352
|
+
msgstr ""
|
353
|
+
|
354
|
+
#: ../lib/r10k/puppetfile.rb:269
|
351
355
|
msgid "unrecognized declaration '%{method}'"
|
352
356
|
msgstr ""
|
353
357
|
|