r10k 3.4.1 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +4 -1
  3. data/.github/workflows/docker.yml +25 -1
  4. data/.github/workflows/rspec_tests.yml +81 -0
  5. data/.travis.yml +19 -8
  6. data/CHANGELOG.mkd +46 -6
  7. data/Gemfile +1 -1
  8. data/README.mkd +13 -4
  9. data/azure-pipelines.yml +4 -2
  10. data/doc/dynamic-environments/configuration.mkd +49 -3
  11. data/doc/faq.mkd +6 -1
  12. data/doc/puppetfile.mkd +2 -0
  13. data/docker/Makefile +19 -3
  14. data/docker/r10k/Dockerfile +22 -8
  15. data/docker/r10k/release.Dockerfile +23 -4
  16. data/integration/tests/git_source/git_source_repeated_remote.rb +68 -0
  17. data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +1 -1
  18. data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +1 -1
  19. data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +1 -1
  20. data/lib/r10k/action/deploy/environment.rb +6 -1
  21. data/lib/r10k/action/deploy/module.rb +2 -1
  22. data/lib/r10k/action/runner.rb +5 -4
  23. data/lib/r10k/cli/deploy.rb +1 -0
  24. data/lib/r10k/environment/base.rb +1 -1
  25. data/lib/r10k/forge/module_release.rb +2 -2
  26. data/lib/r10k/git/cache.rb +12 -4
  27. data/lib/r10k/git/stateful_repository.rb +4 -0
  28. data/lib/r10k/initializers.rb +1 -0
  29. data/lib/r10k/module/base.rb +8 -0
  30. data/lib/r10k/module/git.rb +4 -0
  31. data/lib/r10k/puppetfile.rb +26 -6
  32. data/lib/r10k/settings.rb +12 -1
  33. data/lib/r10k/source.rb +1 -0
  34. data/lib/r10k/source/exec.rb +51 -0
  35. data/lib/r10k/source/git.rb +22 -2
  36. data/lib/r10k/source/hash.rb +32 -8
  37. data/lib/r10k/version.rb +1 -1
  38. data/locales/r10k.pot +33 -10
  39. data/spec/shared-examples/subprocess-runner.rb +11 -5
  40. data/spec/unit/action/deploy/environment_spec.rb +9 -0
  41. data/spec/unit/action/deploy/module_spec.rb +15 -2
  42. data/spec/unit/action/puppetfile/install_spec.rb +4 -1
  43. data/spec/unit/action/runner_spec.rb +2 -2
  44. data/spec/unit/forge/module_release_spec.rb +14 -10
  45. data/spec/unit/git/cache_spec.rb +10 -0
  46. data/spec/unit/git/rugged/credentials_spec.rb +1 -1
  47. data/spec/unit/git_spec.rb +3 -3
  48. data/spec/unit/puppetfile_spec.rb +67 -2
  49. data/spec/unit/settings_spec.rb +12 -0
  50. data/spec/unit/source/exec_spec.rb +81 -0
  51. data/spec/unit/source/git_spec.rb +49 -1
  52. data/spec/unit/source/hash_spec.rb +54 -0
  53. data/spec/unit/source/yaml_spec.rb +42 -0
  54. metadata +8 -2
@@ -1,17 +1,27 @@
1
1
  PUPPERWARE_ANALYTICS_STREAM ?= dev
2
2
  NAMESPACE ?= puppet
3
- git_describe = $(shell git describe)
3
+ git_describe = $(shell git describe --tags)
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 DL3028 --ignore DL4000 --ignore DL4001
7
+ hadolint_command := hadolint
8
8
  hadolint_container := hadolint/hadolint:latest
9
+ alpine_version := 3.9
9
10
  export BUNDLE_PATH = $(PWD)/.bundle/gems
10
11
  export BUNDLE_BIN = $(PWD)/.bundle/bin
11
12
  export GEMFILE = $(PWD)/Gemfile
13
+ export DOCKER_BUILDKIT = 1
12
14
 
13
15
  ifeq ($(IS_RELEASE),true)
14
16
  VERSION ?= $(shell echo $(git_describe) | sed 's/-.*//')
17
+ PUBLISHED_VERSION ?= $(shell curl --silent 'https://rubygems.org/api/v1/gems/r10k.json' | jq '."version"' | tr -d '"')
18
+ CONTAINER_EXISTS = $(shell DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect $(NAMESPACE)/r10k:$(VERSION) > /dev/null 2>&1; echo $$?)
19
+ ifeq ($(CONTAINER_EXISTS),0)
20
+ SKIP_BUILD ?= true
21
+ else ifneq ($(VERSION),$(PUBLISHED_VERSION))
22
+ SKIP_BUILD ?= true
23
+ endif
24
+
15
25
  LATEST_VERSION ?= latest
16
26
  dockerfile := release.Dockerfile
17
27
  dockerfile_context := r10k
@@ -25,6 +35,10 @@ endif
25
35
  prep:
26
36
  @git fetch --unshallow 2> /dev/null ||:
27
37
  @git fetch origin 'refs/tags/*:refs/tags/*'
38
+ ifeq ($(SKIP_BUILD),true)
39
+ @echo "SKIP_BUILD is true, exiting with 1"
40
+ @exit 1
41
+ endif
28
42
 
29
43
  lint:
30
44
  ifeq ($(hadolint_available),0)
@@ -35,8 +49,10 @@ else
35
49
  endif
36
50
 
37
51
  build: prep
52
+ docker pull alpine:$(alpine_version)
38
53
  docker build \
39
- --pull \
54
+ ${DOCKER_BUILD_FLAGS} \
55
+ --build-arg alpine_version=$(alpine_version) \
40
56
  --build-arg vcs_ref=$(vcs_ref) \
41
57
  --build-arg build_date=$(build_date) \
42
58
  --build-arg version=$(VERSION) \
@@ -1,5 +1,7 @@
1
- FROM alpine:3.9 as build
1
+ ARG alpine_version=3.9
2
+ FROM alpine:${alpine_version} as build
2
3
 
4
+ # hadolint ignore=DL3018
3
5
  RUN apk add --no-cache ruby git && \
4
6
  mkdir /workspace
5
7
  WORKDIR /workspace
@@ -7,7 +9,7 @@ COPY . /workspace
7
9
  RUN gem build r10k.gemspec && \
8
10
  mv r10k*.gem r10k.gem
9
11
 
10
- FROM alpine:3.9
12
+ FROM alpine:${alpine_version}
11
13
 
12
14
  ARG vcs_ref
13
15
  ARG build_date
@@ -15,6 +17,11 @@ ARG version="3.1.0"
15
17
  # Used by entrypoint to submit metrics to Google Analytics.
16
18
  # Published images should use "production" for this build_arg.
17
19
  ARG pupperware_analytics_stream="dev"
20
+ # required to schedule runs of "r10k" in K8s
21
+ ARG supercronic_version="0.1.9"
22
+ ARG supercronic_sha1sum="5ddf8ea26b56d4a7ff6faecdd8966610d5cb9d85"
23
+ ARG supercronic="supercronic-linux-amd64"
24
+ ARG supercronic_url="https://github.com/aptible/supercronic/releases/download/v$supercronic_version/$supercronic"
18
25
 
19
26
  LABEL org.label-schema.maintainer="Puppet Release Team <release@puppet.com>" \
20
27
  org.label-schema.vendor="Puppet" \
@@ -31,7 +38,7 @@ COPY docker/r10k/docker-entrypoint.d /docker-entrypoint.d
31
38
  ENTRYPOINT ["/docker-entrypoint.sh"]
32
39
  CMD ["help"]
33
40
 
34
- # dyanmic LABELs and ENV vars placed lower for the sake of Docker layer caching
41
+ # dynamic LABELs and ENV vars placed lower for the sake of Docker layer caching
35
42
  ENV PUPPERWARE_ANALYTICS_STREAM="$pupperware_analytics_stream"
36
43
 
37
44
  LABEL org.label-schema.version="$version" \
@@ -39,14 +46,21 @@ LABEL org.label-schema.version="$version" \
39
46
  org.label-schema.build-date="$build_date"
40
47
 
41
48
  COPY --from=build /workspace/r10k.gem /
42
- RUN chmod a+x /adduser.sh && \
49
+ SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
50
+ # ignore apk and gem pinning
51
+ # hadolint ignore=DL3018,DL3028
52
+ RUN chmod a+x /adduser.sh /docker-entrypoint.sh && \
43
53
  # Add a puppet user to run r10k as for consistency with puppetserver
44
54
  /adduser.sh && \
45
- chmod +x /docker-entrypoint.sh && \
46
55
  chown -R puppet: /docker-entrypoint.d /docker-entrypoint.sh && \
47
- apk add --no-cache ruby openssh-client git ruby-rugged curl ruby-dev make gcc musl-dev && \
48
- gem install --no-doc /r10k.gem json etc && \
49
- rm -f /r10k.gem
56
+ apk add --no-cache ruby openssh-client git ruby-rugged curl ruby-json ruby-etc && \
57
+ gem install --no-doc /r10k.gem && \
58
+ rm -f /r10k.gem && \
59
+ curl --fail --silent --show-error --location --remote-name "$supercronic_url" && \
60
+ echo "${supercronic_sha1sum} ${supercronic}" | sha1sum -c - && \
61
+ chmod +x "$supercronic" && \
62
+ mv "$supercronic" "/usr/local/bin/${supercronic}" && \
63
+ ln -s "/usr/local/bin/${supercronic}" /usr/local/bin/supercronic
50
64
 
51
65
  USER puppet
52
66
  WORKDIR /home/puppet
@@ -1,4 +1,5 @@
1
- FROM alpine:3.9
1
+ ARG alpine_version=3.9
2
+ FROM alpine:${alpine_version}
2
3
 
3
4
  ARG vcs_ref
4
5
  ARG build_date
@@ -6,6 +7,11 @@ ARG version="3.1.0"
6
7
  # Used by entrypoint to submit metrics to Google Analytics.
7
8
  # Published images should use "production" for this build_arg.
8
9
  ARG pupperware_analytics_stream="dev"
10
+ # required to schedule runs of "r10k" in K8s
11
+ ARG supercronic_version="0.1.9"
12
+ ARG supercronic_sha1sum="5ddf8ea26b56d4a7ff6faecdd8966610d5cb9d85"
13
+ ARG supercronic="supercronic-linux-amd64"
14
+ ARG supercronic_url="https://github.com/aptible/supercronic/releases/download/v$supercronic_version/$supercronic"
9
15
 
10
16
  LABEL org.label-schema.maintainer="Puppet Release Team <release@puppet.com>" \
11
17
  org.label-schema.vendor="Puppet" \
@@ -16,7 +22,7 @@ LABEL org.label-schema.maintainer="Puppet Release Team <release@puppet.com>" \
16
22
  org.label-schema.schema-version="1.0" \
17
23
  org.label-schema.dockerfile="/release.Dockerfile"
18
24
 
19
- COPY docker-entrypoint.sh /
25
+ COPY adduser.sh docker-entrypoint.sh /
20
26
  COPY docker-entrypoint.d /docker-entrypoint.d
21
27
 
22
28
  ENTRYPOINT ["/docker-entrypoint.sh"]
@@ -29,8 +35,21 @@ LABEL org.label-schema.version="$version" \
29
35
  org.label-schema.vcs-ref="$vcs_ref" \
30
36
  org.label-schema.build-date="$build_date"
31
37
 
32
- RUN chmod +x /docker-entrypoint.sh && \
38
+ SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
39
+ # ignore apk and gem pinning
40
+ # hadolint ignore=DL3018,DL3028
41
+ RUN chmod a+x /adduser.sh /docker-entrypoint.sh && \
42
+ /adduser.sh && \
43
+ chown -R puppet: /docker-entrypoint.d /docker-entrypoint.sh && \
33
44
  apk add --no-cache ruby openssh-client git ruby-rugged curl ruby-dev make gcc musl-dev && \
34
- gem install --no-doc r10k:"$version" json etc
45
+ gem install --no-doc r10k:"$version" json etc && \
46
+ curl --fail --silent --show-error --location --remote-name "$supercronic_url" && \
47
+ echo "${supercronic_sha1sum} ${supercronic}" | sha1sum -c - && \
48
+ chmod +x "$supercronic" && \
49
+ mv "$supercronic" "/usr/local/bin/${supercronic}" && \
50
+ ln -s "/usr/local/bin/${supercronic}" /usr/local/bin/supercronic
51
+
52
+ USER puppet
53
+ WORKDIR /home/puppet
35
54
 
36
55
  COPY release.Dockerfile /
@@ -0,0 +1,68 @@
1
+ require 'git_utils'
2
+ require 'r10k_utils'
3
+ require 'master_manipulator'
4
+ test_name 'Verify the same remote can be used in more than one object'
5
+
6
+ env_path = on(master, puppet('config print environmentpath')).stdout.rstrip
7
+ r10k_fqp = get_r10k_fqp(master)
8
+ git_environments_path = '/root/environments'
9
+ git_repo_path = '/git_repos'
10
+ git_repo_name = 'environments'
11
+ git_control_remote = File.join(git_repo_path, "#{git_repo_name}.git")
12
+ code_dir = "#{env_path}/production"
13
+
14
+ last_commit = git_last_commit(master, git_environments_path)
15
+ git_provider = ENV['GIT_PROVIDER']
16
+ r10k_config_path = get_r10k_config_file_path(master)
17
+ r10k_config_bak_path = "#{r10k_config_path}.bak"
18
+ #In-line files
19
+ r10k_conf = <<-CONF
20
+ cachedir: '/var/cache/r10k'
21
+ git:
22
+ provider: '#{git_provider}'
23
+ sources:
24
+ control:
25
+ basedir: "#{env_path}"
26
+ remote: "#{git_control_remote}"
27
+ CONF
28
+
29
+ # Install the same module in two different places
30
+ puppetfile = <<-EOS
31
+ mod 'prod_apache',
32
+ :git => 'git://github.com/puppetlabs/puppetlabs-apache.git',
33
+ :branch => 'master'
34
+
35
+ mod 'test_apache',
36
+ :git => 'git://github.com/puppetlabs/puppetlabs-apache.git',
37
+ :branch => 'master'
38
+ EOS
39
+
40
+ teardown do
41
+ step 'Restore Original "r10k" Config'
42
+ on(master, "mv #{r10k_config_bak_path} #{r10k_config_path}")
43
+
44
+ clean_up_r10k(master, last_commit, git_environments_path)
45
+ end
46
+
47
+ step 'Stub the forge'
48
+ stub_forge_on(master)
49
+
50
+ step 'Backup Current "r10k" Config'
51
+ on(master, "mv #{r10k_config_path} #{r10k_config_bak_path}")
52
+
53
+ step 'Update the "r10k" Config'
54
+ create_remote_file(master, r10k_config_path, r10k_conf)
55
+
56
+ step 'Ask r10k to deploy'
57
+ on(master, "#{r10k_fqp} deploy environment -p")
58
+
59
+ step 'Add puppetfile with repeated remote'
60
+ create_remote_file(master, "#{git_environments_path}/Puppetfile", puppetfile)
61
+ git_add_commit_push(master, 'production', 'add Puppetfile', git_environments_path)
62
+
63
+ step 'Deploy r10k'
64
+ on(master, "#{r10k_fqp} deploy environment -p")
65
+
66
+ step 'Verify module was installed in both places'
67
+ on(master, "test -d #{code_dir}/modules/prod_apache")
68
+ on(master, "test -d #{code_dir}/modules/test_apache")
@@ -24,7 +24,7 @@ stage_env_notify_message = 'This is a different message'
24
24
  stage_env_notify_message_regex = /#{stage_env_notify_message}/
25
25
 
26
26
  #Verification for "test" Environment
27
- test_env_error_message_regex = /Error:.*Could not find environment 'test'/
27
+ test_env_error_message_regex = /Error:.*Could not.*environment '?test'?/
28
28
 
29
29
  #Verification for "temp" Environment
30
30
  test_env_notify_message_regex = /I am in the temp environment/
@@ -15,7 +15,7 @@ initial_env_names = ['production', 'stage']
15
15
 
16
16
  #Verification
17
17
  notify_message_regex = /I am in the production environment/
18
- stage_env_error_message_regex = /Error:.*Could not find environment 'stage'/
18
+ stage_env_error_message_regex = /Error:.*Could not.*environment '?stage'?/
19
19
 
20
20
  #Manifest
21
21
  site_pp_path = File.join(git_environments_path, 'manifests', 'site.pp')
@@ -22,7 +22,7 @@ site_pp = create_site_pp(master_certname, ' include helloworld')
22
22
  notify_message_prod_env_regex = /I am in the production environment/
23
23
  notify_message_test_env_regex = /I am in the test environment/
24
24
  removal_message_test_env_regex = /Removing unmanaged path.*test/
25
- error_message_regex = /Could not retrieve catalog from remote server/
25
+ error_message_regex = /Could not retrieve (catalog from remote server|information from environment test)/
26
26
 
27
27
  #Teardown
28
28
  teardown do
@@ -157,7 +157,10 @@ module R10K
157
157
  logger.debug("Unable to get environment module deploy data for .r10k-deploy.json at #{environment.path}")
158
158
  end
159
159
 
160
- File.open("#{environment.path}/.r10k-deploy.json", 'w') do |f|
160
+ # make this file write as atomic as possible in pure ruby
161
+ final = "#{environment.path}/.r10k-deploy.json"
162
+ staging = "#{environment.path}/.r10k-deploy.json~"
163
+ File.open(staging, 'w') do |f|
161
164
  deploy_info = environment.info.merge({
162
165
  :started_at => started_at,
163
166
  :finished_at => Time.new,
@@ -167,6 +170,7 @@ module R10K
167
170
 
168
171
  f.puts(JSON.pretty_generate(deploy_info))
169
172
  end
173
+ FileUtils.mv(staging, final)
170
174
  end
171
175
 
172
176
  def undeployable_environment_names(environments, expected_names)
@@ -184,6 +188,7 @@ module R10K
184
188
  'no-force': :self,
185
189
  'generate-types': :self,
186
190
  'puppet-path': :self,
191
+ 'puppet-conf': :self,
187
192
  'default-branch-override': :self)
188
193
  end
189
194
  end
@@ -76,7 +76,8 @@ module R10K
76
76
  cachedir: :self,
77
77
  'no-force': :self,
78
78
  'generate-types': :self,
79
- 'puppet-path': :self)
79
+ 'puppet-path': :self,
80
+ 'puppet-conf': :self)
80
81
  end
81
82
  end
82
83
  end
@@ -43,10 +43,11 @@ module R10K
43
43
  config_settings = settings_from_config(@opts[:config])
44
44
 
45
45
  overrides = {}
46
- overrides[:cachedir] = @opts[:cachedir] unless @opts[:cachedir].nil?
47
- overrides[:deploy] = {} if !@opts[:'puppet-path'].nil? || !@opts[:'generate-types'].nil?
48
- overrides[:deploy][:puppet_path] = @opts[:'puppet-path'] unless @opts[:'puppet-path'].nil?
49
- overrides[:deploy][:generate_types] = @opts[:'generate-types'] unless @opts[:'generate-types'].nil?
46
+ overrides[:cachedir] = @opts[:cachedir] if @opts.key?(:cachedir)
47
+ overrides[:deploy] = {} if @opts.key?(:'puppet-path') || @opts.key?(:'generate-types')
48
+ overrides[:deploy][:puppet_path] = @opts[:'puppet-path'] if @opts.key?(:'puppet-path')
49
+ overrides[:deploy][:puppet_conf] = @opts[:'puppet-conf'] unless @opts[:'puppet-conf'].nil?
50
+ overrides[:deploy][:generate_types] = @opts[:'generate-types'] if @opts.key?(:'generate-types')
50
51
 
51
52
  with_overrides = config_settings.merge(overrides) do |key, oldval, newval|
52
53
  newval = oldval.merge(newval) if oldval.is_a? Hash
@@ -31,6 +31,7 @@ module R10K::CLI
31
31
  exit 1
32
32
  end
33
33
  end
34
+ option nil, :'puppet-conf', 'Path to puppet.conf', argument: :required
34
35
 
35
36
  run do |opts, args, cmd|
36
37
  puts cmd.help(:verbose => opts[:verbose])
@@ -137,7 +137,7 @@ class R10K::Environment::Base
137
137
  end
138
138
 
139
139
  def generate_types!
140
- argv = [R10K::Settings.puppet_path, 'generate', 'types', '--environment', dirname, '--environmentpath', basedir]
140
+ argv = [R10K::Settings.puppet_path, 'generate', 'types', '--environment', dirname, '--environmentpath', basedir, '--config', R10K::Settings.puppet_conf]
141
141
  subproc = R10K::Util::Subprocess.new(argv)
142
142
  subproc.raise_on_fail = true
143
143
  subproc.logger = logger
@@ -212,14 +212,14 @@ module R10K
212
212
 
213
213
  # Remove the temporary directory used for unpacking the module.
214
214
  def cleanup_unpack_path
215
- if unpack_path.exist?
215
+ if unpack_path.parent.exist?
216
216
  unpack_path.parent.rmtree
217
217
  end
218
218
  end
219
219
 
220
220
  # Remove the downloaded module release.
221
221
  def cleanup_download_path
222
- if download_path.exist?
222
+ if download_path.parent.exist?
223
223
  download_path.parent.rmtree
224
224
  end
225
225
  end
@@ -16,7 +16,17 @@ class R10K::Git::Cache
16
16
 
17
17
  include R10K::Settings::Mixin
18
18
 
19
- def_setting_attr :cache_root, File.expand_path(ENV['HOME'] ? '~/.r10k/git': '/root/.r10k/git')
19
+ #@api private
20
+ def self.determine_cache_root
21
+ if R10K::Util::Platform.windows?
22
+ File.join(ENV['LOCALAPPDATA'], 'r10k', 'git')
23
+ else
24
+ File.expand_path(ENV['HOME'] ? '~/.r10k/git': '/root/.r10k/git')
25
+ end
26
+ end
27
+ private_class_method :determine_cache_root
28
+
29
+ def_setting_attr :cache_root, determine_cache_root
20
30
 
21
31
  @instance_cache = R10K::InstanceCache.new(self)
22
32
 
@@ -99,10 +109,8 @@ class R10K::Git::Cache
99
109
 
100
110
  alias cached? exist?
101
111
 
102
- private
103
-
104
112
  # Reformat the remote name into something that can be used as a directory
105
113
  def sanitized_dirname
106
- @remote.gsub(/[^@\w\.-]/, '-')
114
+ @sanitized_dirname ||= @remote.gsub(/[^@\w\.-]/, '-')
107
115
  end
108
116
  end
@@ -12,6 +12,10 @@ class R10K::Git::StatefulRepository
12
12
  # @api private
13
13
  attr_reader :repo
14
14
 
15
+ # @!attribute [r] cache
16
+ # @api private
17
+ attr_reader :cache
18
+
15
19
  extend Forwardable
16
20
  def_delegators :@repo, :head, :tracked_paths
17
21
 
@@ -44,6 +44,7 @@ module R10K
44
44
  class DeployInitializer < BaseInitializer
45
45
  def call
46
46
  with_setting(:puppet_path) { |value| R10K::Settings.puppet_path = value }
47
+ with_setting(:puppet_conf) { |value| R10K::Settings.puppet_conf = value }
47
48
  end
48
49
  end
49
50
 
@@ -99,6 +99,14 @@ class R10K::Module::Base
99
99
  raise NotImplementedError
100
100
  end
101
101
 
102
+ # Return the module's cachedir. Subclasses that implement a cache
103
+ # will override this to return a real directory location.
104
+ #
105
+ # @return [String, :none]
106
+ def cachedir
107
+ :none
108
+ end
109
+
102
110
  private
103
111
 
104
112
  def parse_title(title)
@@ -57,6 +57,10 @@ class R10K::Module::Git < R10K::Module::Base
57
57
  @repo.status(version)
58
58
  end
59
59
 
60
+ def cachedir
61
+ @repo.cache.sanitized_dirname
62
+ end
63
+
60
64
  private
61
65
 
62
66
  def validate_ref(desired, default)
@@ -10,7 +10,7 @@ class Puppetfile
10
10
 
11
11
  include R10K::Settings::Mixin
12
12
 
13
- def_setting_attr :pool_size, 1
13
+ def_setting_attr :pool_size, 4
14
14
 
15
15
  include R10K::Logging
16
16
 
@@ -42,6 +42,11 @@ class Puppetfile
42
42
  # @return [Boolean] Overwrite any locally made changes
43
43
  attr_accessor :force
44
44
 
45
+ # @!attribute [r] modules_by_vcs_cachedir
46
+ # @api private Only exposed for testing purposes
47
+ # @return [Hash{:none, String => Array<R10K::Module>}]
48
+ attr_reader :modules_by_vcs_cachedir
49
+
45
50
  # @param [String] basedir
46
51
  # @param [String] moduledir The directory to install the modules, default to #{basedir}/modules
47
52
  # @param [String] puppetfile_path The path to the Puppetfile, default to #{basedir}/Puppetfile
@@ -58,6 +63,7 @@ class Puppetfile
58
63
 
59
64
  @modules = []
60
65
  @managed_content = {}
66
+ @modules_by_vcs_cachedir = {}
61
67
  @forge = 'forgeapi.puppetlabs.com'
62
68
 
63
69
  @loaded = false
@@ -137,6 +143,9 @@ class Puppetfile
137
143
  mod.origin = 'Puppetfile'
138
144
 
139
145
  @managed_content[install_path] << mod.name
146
+ cachedir = mod.cachedir
147
+ @modules_by_vcs_cachedir[cachedir] ||= []
148
+ @modules_by_vcs_cachedir[cachedir] << mod
140
149
  @modules << mod
141
150
  end
142
151
 
@@ -145,7 +154,9 @@ class Puppetfile
145
154
  def managed_directories
146
155
  self.load unless @loaded
147
156
 
148
- @managed_content.keys
157
+ dirs = @managed_content.keys
158
+ dirs.delete(real_basedir)
159
+ dirs
149
160
  end
150
161
 
151
162
  # Returns an array of the full paths to all the content being managed.
@@ -212,15 +223,22 @@ class Puppetfile
212
223
  def modules_queue(visitor)
213
224
  Queue.new.tap do |queue|
214
225
  visitor.visit(:puppetfile, self) do
215
- modules.each { |mod| queue << mod }
226
+ modules_by_cachedir = modules_by_vcs_cachedir.clone
227
+ modules_without_vcs_cachedir = modules_by_cachedir.delete(:none) || []
228
+
229
+ modules_without_vcs_cachedir.each {|mod| queue << Array(mod) }
230
+ modules_by_cachedir.values.each {|mods| queue << mods }
216
231
  end
217
232
  end
218
233
  end
234
+ public :modules_queue
219
235
 
220
236
  def visitor_thread(visitor, mods_queue)
221
237
  Thread.new do
222
238
  begin
223
- while mod = mods_queue.pop(true) do mod.accept(visitor) end
239
+ while mods = mods_queue.pop(true) do
240
+ mods.each {|mod| mod.accept(visitor) }
241
+ end
224
242
  rescue ThreadError => e
225
243
  logger.debug _("Module thread %{id} exiting: %{message}") % {message: e.message, id: Thread.current.object_id}
226
244
  Thread.exit
@@ -248,8 +266,6 @@ class Puppetfile
248
266
  end
249
267
 
250
268
  def validate_install_path(path, modname)
251
- real_basedir = Pathname.new(basedir).cleanpath.to_s
252
-
253
269
  unless /^#{Regexp.escape(real_basedir)}.*/ =~ path
254
270
  raise R10K::Error.new("Puppetfile cannot manage content '#{modname}' outside of containing environment: #{path} is not within #{real_basedir}")
255
271
  end
@@ -257,6 +273,10 @@ class Puppetfile
257
273
  true
258
274
  end
259
275
 
276
+ def real_basedir
277
+ Pathname.new(basedir).cleanpath.to_s
278
+ end
279
+
260
280
  class DSL
261
281
  # A barebones implementation of the Puppetfile DSL
262
282
  #