r10k 3.8.0 → 3.9.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docker.yml +4 -1
  3. data/.github/workflows/release.yml +3 -2
  4. data/.github/workflows/rspec_tests.yml +1 -1
  5. data/.travis.yml +8 -1
  6. data/CHANGELOG.mkd +10 -0
  7. data/CODEOWNERS +1 -1
  8. data/doc/common-patterns.mkd +1 -0
  9. data/doc/dynamic-environments/configuration.mkd +90 -43
  10. data/doc/dynamic-environments/usage.mkd +7 -7
  11. data/doc/puppetfile.mkd +23 -3
  12. data/docker/Gemfile +1 -1
  13. data/docker/Makefile +4 -3
  14. data/docker/docker-compose.yml +18 -0
  15. data/docker/r10k/Dockerfile +1 -1
  16. data/docker/r10k/docker-entrypoint.sh +0 -1
  17. data/docker/r10k/release.Dockerfile +1 -1
  18. data/docker/spec/dockerfile_spec.rb +26 -32
  19. data/integration/tests/git_source/git_source_repeated_remote.rb +2 -2
  20. data/integration/tests/user_scenario/basic_workflow/multi_env_custom_forge_git_module.rb +2 -1
  21. data/integration/tests/user_scenario/basic_workflow/multi_env_custom_forge_git_module_static.rb +2 -1
  22. data/integration/tests/user_scenario/basic_workflow/multi_source_custom_forge_git_module.rb +1 -1
  23. data/integration/tests/user_scenario/basic_workflow/single_env_custom_forge_git_module.rb +2 -1
  24. data/lib/r10k/action/deploy/display.rb +9 -3
  25. data/lib/r10k/action/deploy/environment.rb +36 -14
  26. data/lib/r10k/cli/deploy.rb +5 -3
  27. data/lib/r10k/environment/base.rb +1 -1
  28. data/lib/r10k/environment/git.rb +17 -2
  29. data/lib/r10k/environment/name.rb +22 -4
  30. data/lib/r10k/environment/svn.rb +11 -2
  31. data/lib/r10k/environment/with_modules.rb +1 -1
  32. data/lib/r10k/git/rugged/credentials.rb +22 -15
  33. data/lib/r10k/module/forge.rb +15 -3
  34. data/lib/r10k/module/git.rb +24 -23
  35. data/lib/r10k/module/local.rb +1 -1
  36. data/lib/r10k/module/svn.rb +14 -11
  37. data/lib/r10k/settings.rb +6 -1
  38. data/lib/r10k/source/base.rb +5 -0
  39. data/lib/r10k/source/git.rb +4 -1
  40. data/lib/r10k/source/hash.rb +4 -2
  41. data/lib/r10k/source/svn.rb +5 -1
  42. data/lib/r10k/util/setopts.rb +33 -12
  43. data/lib/r10k/version.rb +1 -1
  44. data/locales/r10k.pot +22 -18
  45. data/r10k.gemspec +1 -1
  46. data/spec/unit/action/deploy/display_spec.rb +4 -0
  47. data/spec/unit/action/deploy/environment_spec.rb +111 -10
  48. data/spec/unit/environment/git_spec.rb +16 -0
  49. data/spec/unit/environment/name_spec.rb +28 -0
  50. data/spec/unit/environment/svn_spec.rb +12 -0
  51. data/spec/unit/git/rugged/credentials_spec.rb +10 -0
  52. data/spec/unit/module/forge_spec.rb +6 -0
  53. data/spec/unit/module/git_spec.rb +1 -1
  54. data/spec/unit/module_spec.rb +59 -9
  55. data/spec/unit/util/setopts_spec.rb +25 -1
  56. metadata +5 -11
  57. data/azure-pipelines.yml +0 -87
@@ -0,0 +1,18 @@
1
+ version: '3.7'
2
+
3
+ services:
4
+ r10k_check:
5
+ image: ${R10K_IMAGE:-puppet/r10k}
6
+ environment:
7
+ - PUPPERWARE_ANALYTICS_ENABLED=${PUPPERWARE_ANALYTICS_ENABLED:-false}
8
+ command: 'puppetfile check --verbose --trace --puppetfile test/Puppetfile'
9
+ volumes:
10
+ - ${SPEC_DIRECTORY}/fixtures:/home/puppet/test
11
+
12
+ r10k_install:
13
+ image: ${R10K_IMAGE:-puppet/r10k}
14
+ environment:
15
+ - PUPPERWARE_ANALYTICS_ENABLED=${PUPPERWARE_ANALYTICS_ENABLED:-false}
16
+ command: 'puppetfile install --verbose --trace --puppetfile test/Puppetfile'
17
+ volumes:
18
+ - ${SPEC_DIRECTORY}/fixtures:/home/puppet/test
@@ -49,7 +49,7 @@ COPY --from=build /workspace/r10k.gem /
49
49
  SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
50
50
  # ignore apk and gem pinning
51
51
  # hadolint ignore=DL3018,DL3028
52
- RUN chmod a+x /adduser.sh /docker-entrypoint.sh && \
52
+ RUN chmod a+x /adduser.sh /docker-entrypoint.sh /docker-entrypoint.d/*.sh && \
53
53
  # Add a puppet user to run r10k as for consistency with puppetserver
54
54
  /adduser.sh && \
55
55
  chown -R puppet: /docker-entrypoint.d /docker-entrypoint.sh && \
@@ -4,7 +4,6 @@ set -e
4
4
 
5
5
  for f in /docker-entrypoint.d/*.sh; do
6
6
  # Don't print out any messages here since this is a CLI container
7
- chmod +x "$f"
8
7
  "$f"
9
8
  done
10
9
 
@@ -38,7 +38,7 @@ LABEL org.label-schema.version="$version" \
38
38
  SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
39
39
  # ignore apk and gem pinning
40
40
  # hadolint ignore=DL3018,DL3028
41
- RUN chmod a+x /adduser.sh /docker-entrypoint.sh && \
41
+ RUN chmod a+x /adduser.sh /docker-entrypoint.sh /docker-entrypoint.d/*.sh && \
42
42
  /adduser.sh && \
43
43
  chown -R puppet: /docker-entrypoint.d /docker-entrypoint.sh && \
44
44
  apk add --no-cache ruby openssh-client git ruby-rugged curl ruby-dev make gcc musl-dev && \
@@ -1,43 +1,37 @@
1
1
  require 'rspec/core'
2
2
  require 'fileutils'
3
3
  require 'open3'
4
+ include Pupperware::SpecHelpers
4
5
 
5
- SPEC_DIRECTORY = File.dirname(__FILE__)
6
+ ENV['SPEC_DIRECTORY'] = File.dirname(__FILE__)
7
+ # unifies volume naming
8
+ ENV['COMPOSE_PROJECT_NAME'] ||= 'r10k'
6
9
 
7
- describe 'r10k container' do
8
- include Pupperware::SpecHelpers
9
- def run_r10k(command)
10
- run_command("docker run --detach \
11
- --volume #{File.join(SPEC_DIRECTORY, 'fixtures')}:/home/puppet/test \
12
- #{@image} #{command} \
13
- --verbose \
14
- --trace \
15
- --puppetfile test/Puppetfile")
16
- end
17
-
18
- before(:all) do
19
- @image = require_test_image
10
+ RSpec.configure do |c|
11
+ c.before(:suite) do
12
+ ENV['R10K_IMAGE'] = require_test_image
13
+ pull_images(['r10k_check','r10k_install'])
14
+ teardown_cluster()
15
+ # no certs to preload, but if the suite adds puppetserver, be explicit
16
+ docker_compose_up(preload_certs: true)
20
17
  end
21
18
 
22
- after(:all) do
23
- FileUtils.rm_rf(File.join(SPEC_DIRECTORY, 'fixtures', 'modules'))
24
- end
25
-
26
- it 'should validate the Puppetfile' do
27
- result = run_r10k('puppetfile check')
28
- container = result[:stdout].chomp
29
- wait_on_container_exit(container)
30
- expect(get_container_exit_code(container)).to eq(0)
31
- emit_log(container)
32
- teardown_container(container)
19
+ c.after(:suite) do
20
+ teardown_cluster()
21
+ FileUtils.rm_rf(File.join(ENV['SPEC_DIRECTORY'], 'fixtures', 'modules'))
33
22
  end
23
+ end
34
24
 
35
- it 'should install the Puppetfile' do
36
- result = run_r10k('puppetfile install')
37
- container = result[:stdout].chomp
38
- wait_on_container_exit(container)
39
- expect(get_container_exit_code(container)).to eq(0)
40
- emit_log(container)
41
- teardown_container(container)
25
+ describe 'r10k container' do
26
+ {
27
+ 'r10k_check': 'validate',
28
+ 'r10k_install': 'install',
29
+ }.each do |container, op|
30
+ it "should #{op} the Puppetfile" do
31
+ container = get_service_container(container)
32
+ wait_on_container_exit(container)
33
+ expect(get_container_exit_code(container)).to eq(0)
34
+ emit_log(container)
35
+ end
42
36
  end
43
37
  end
@@ -30,11 +30,11 @@ CONF
30
30
  puppetfile = <<-EOS
31
31
  mod 'prod_apache',
32
32
  :git => 'git://github.com/puppetlabs/puppetlabs-apache.git',
33
- :branch => 'master'
33
+ :tag => 'v6.0.0'
34
34
 
35
35
  mod 'test_apache',
36
36
  :git => 'git://github.com/puppetlabs/puppetlabs-apache.git',
37
- :branch => 'master'
37
+ :tag => 'v6.0.0'
38
38
  EOS
39
39
 
40
40
  teardown do
@@ -27,7 +27,8 @@ stdlib_notify_message_regex = /The test message is:.*one.*=>.*1.*two.*=>.*bats.*
27
27
  puppet_file = <<-PUPPETFILE
28
28
  mod "puppetlabs/motd"
29
29
  mod 'puppetlabs/stdlib',
30
- :git => 'git://github.com/puppetlabs/puppetlabs-stdlib.git'
30
+ :git => 'git://github.com/puppetlabs/puppetlabs-stdlib.git',
31
+ :tag => 'v7.0.1'
31
32
  PUPPETFILE
32
33
 
33
34
  puppet_file_path = File.join(git_environments_path, 'Puppetfile')
@@ -31,7 +31,8 @@ puppet_file = <<-PUPPETFILE
31
31
  moduledir '#{@module_path}'
32
32
  mod "puppetlabs/motd"
33
33
  mod 'puppetlabs/stdlib',
34
- :git => 'git://github.com/puppetlabs/puppetlabs-stdlib.git'
34
+ :git => 'git://github.com/puppetlabs/puppetlabs-stdlib.git',
35
+ :tag => 'v7.0.1'
35
36
  PUPPETFILE
36
37
 
37
38
  puppet_file_path = File.join(git_environments_path, 'Puppetfile')
@@ -65,7 +65,7 @@ env_structs = {:production => GitEnv.new('/git_repos',
65
65
  '/git_repos_alt/environments_alt.git',
66
66
  '/root/environments_alt',
67
67
  '/root/environments_alt/Puppetfile',
68
- 'mod "puppetlabs/stdlib", :git => "git://github.com/puppetlabs/puppetlabs-stdlib.git"',
68
+ 'mod "puppetlabs/stdlib", :git => "git://github.com/puppetlabs/puppetlabs-stdlib.git", :tag => "v7.0.1"',
69
69
  '/root/environments_alt/manifests/site.pp',
70
70
  create_site_pp(master_certname, stage_env_manifest)
71
71
  ),
@@ -28,7 +28,8 @@ notify_message_regex = /I am in the production environment/
28
28
  puppet_file = <<-PUPPETFILE
29
29
  mod "puppetlabs/motd"
30
30
  mod 'puppetlabs/inifile',
31
- :git => 'git://github.com/puppetlabs/puppetlabs-inifile'
31
+ :git => 'git://github.com/puppetlabs/puppetlabs-inifile',
32
+ :tag => 'v5.0.1'
32
33
  PUPPETFILE
33
34
 
34
35
  puppet_file_path = File.join(git_environments_path, 'Puppetfile')
@@ -59,14 +59,14 @@ module R10K
59
59
  end
60
60
 
61
61
  def environment_info(env)
62
- if !@puppetfile && !@detail
62
+ if !@modules && !@detail
63
63
  env.dirname
64
64
  else
65
65
  env_info = env.info.merge({
66
66
  :status => (env.status rescue nil),
67
67
  })
68
68
 
69
- env_info[:modules] = env.modules.map { |mod| module_info(mod) } if @puppetfile
69
+ env_info[:modules] = env.modules.map { |mod| module_info(mod) } if @modules
70
70
 
71
71
  env_info
72
72
  end
@@ -81,7 +81,13 @@ module R10K
81
81
  end
82
82
 
83
83
  def allowed_initialize_opts
84
- super.merge(puppetfile: :self, detail: :self, format: :self, fetch: :self)
84
+ super.merge({
85
+ puppetfile: :modules,
86
+ modules: :self,
87
+ detail: :self,
88
+ format: :self,
89
+ fetch: :self
90
+ })
85
91
  end
86
92
  end
87
93
  end
@@ -18,7 +18,8 @@ module R10K
18
18
  def initialize(opts, argv, settings = nil)
19
19
  settings ||= {}
20
20
  @purge_levels = settings.fetch(:deploy, {}).fetch(:purge_levels, [])
21
- @user_purge_whitelist = settings.fetch(:deploy, {}).fetch(:purge_whitelist, [])
21
+ @user_purge_allowlist = read_purge_allowlist(settings.fetch(:deploy, {}).fetch(:purge_whitelist, []),
22
+ settings.fetch(:deploy, {}).fetch(:purge_allowlist, []))
22
23
  @generate_types = settings.fetch(:deploy, {}).fetch(:generate_types, false)
23
24
 
24
25
  super
@@ -43,6 +44,23 @@ module R10K
43
44
 
44
45
  private
45
46
 
47
+ def read_purge_allowlist (whitelist, allowlist)
48
+ whitelist_has_content = !whitelist.empty?
49
+ allowlist_has_content = !allowlist.empty?
50
+ case
51
+ when whitelist_has_content == false && allowlist_has_content == false
52
+ []
53
+ when whitelist_has_content && allowlist_has_content
54
+ raise R10K::Error.new "Values found for both purge_whitelist and purge_allowlist. Setting " <<
55
+ "purge_whitelist is deprecated, please only use purge_allowlist."
56
+ when allowlist_has_content
57
+ allowlist
58
+ else
59
+ logger.warn "Setting purge_whitelist is deprecated; please use purge_allowlist instead."
60
+ whitelist
61
+ end
62
+ end
63
+
46
64
  def visit_deployment(deployment)
47
65
  # Ensure that everything can be preloaded. If we cannot preload all
48
66
  # sources then we can't fully enumerate all environments which
@@ -95,7 +113,7 @@ module R10K
95
113
  environment.sync
96
114
  logger.info _("Environment %{env_dir} is now at %{env_signature}") % {env_dir: environment.dirname, env_signature: environment.signature}
97
115
 
98
- if status == :absent || @puppetfile
116
+ if status == :absent || @modules
99
117
  if status == :absent
100
118
  logger.debug(_("Environment %{env_dir} is new, updating all modules") % {env_dir: environment.dirname})
101
119
  end
@@ -110,7 +128,7 @@ module R10K
110
128
  if @purge_levels.include?(:environment)
111
129
  if @visit_ok
112
130
  logger.debug("Purging unmanaged content for environment '#{environment.dirname}'...")
113
- environment.purge!(:recurse => true, :whitelist => environment.whitelist(@user_purge_whitelist))
131
+ environment.purge!(:recurse => true, :whitelist => environment.whitelist(@user_purge_allowlist))
114
132
  else
115
133
  logger.debug("Not purging unmanaged content for environment '#{environment.dirname}' due to prior deploy failures.")
116
134
  end
@@ -145,17 +163,20 @@ module R10K
145
163
  end
146
164
 
147
165
  def write_environment_info!(environment, started_at, success)
148
- module_deploys = []
149
- begin
150
- environment.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})
166
+ module_deploys =
167
+ begin
168
+ environment.modules.map do |mod|
169
+ props = mod.properties
170
+ {
171
+ name: mod.name,
172
+ version: props[:expected],
173
+ sha: props[:type] == :git ? props[:actual] : nil
174
+ }
175
+ end
176
+ rescue
177
+ logger.debug("Unable to get environment module deploy data for .r10k-deploy.json at #{environment.path}")
178
+ []
155
179
  end
156
- rescue
157
- logger.debug("Unable to get environment module deploy data for .r10k-deploy.json at #{environment.path}")
158
- end
159
180
 
160
181
  # make this file write as atomic as possible in pure ruby
161
182
  final = "#{environment.path}/.r10k-deploy.json"
@@ -183,7 +204,8 @@ module R10K
183
204
  end
184
205
 
185
206
  def allowed_initialize_opts
186
- super.merge(puppetfile: :self,
207
+ super.merge(puppetfile: :modules,
208
+ modules: :self,
187
209
  cachedir: :self,
188
210
  'no-force': :self,
189
211
  'generate-types': :self,
@@ -55,7 +55,7 @@ branches.
55
55
 
56
56
  Environments can provide a Puppetfile at the root of the directory to deploy
57
57
  independent Puppet modules. To recursively deploy an environment, pass the
58
- `--puppetfile` flag to the command.
58
+ `--modules` flag to the command.
59
59
 
60
60
  **NOTE**: If an environment has a Puppetfile when it is instantiated a
61
61
  recursive update will be forced. It is assumed that environments are dependent
@@ -63,7 +63,8 @@ on modules specified in the Puppetfile and an update will be automatically
63
63
  scheduled. On subsequent deployments, Puppetfile deployment will default to off.
64
64
  DESCRIPTION
65
65
 
66
- flag :p, :puppetfile, 'Deploy modules from a puppetfile'
66
+ flag :p, :puppetfile, 'Deploy modules (deprecated, use -m)'
67
+ flag :m, :modules, 'Deploy modules'
67
68
  option nil, :'default-branch-override', 'Specify a branchname to override the default branch in the puppetfile',
68
69
  argument: :required
69
70
 
@@ -100,7 +101,8 @@ try to deploy the given module names in all environments.
100
101
  usage 'display'
101
102
  summary 'Display environments and modules in the deployment'
102
103
 
103
- flag :p, :puppetfile, 'Display Puppetfile modules'
104
+ flag :p, :puppetfile, 'Display modules (deprecated, use -m)'
105
+ flag :m, :modules, 'Display modules'
104
106
  flag nil, :detail, 'Display detailed information'
105
107
  flag nil, :fetch, 'Update available environment lists from all remote sources'
106
108
  option nil, :format, 'Display output in a specific format. Valid values: json, yaml. Default: yaml',
@@ -43,7 +43,7 @@ class R10K::Environment::Base
43
43
  @basedir = basedir
44
44
  @dirname = dirname
45
45
  @options = options
46
- @puppetfile_name = options[:puppetfile_name]
46
+ @puppetfile_name = options.delete(:puppetfile_name)
47
47
 
48
48
  @full_path = File.join(@basedir, @dirname)
49
49
  @path = Pathname.new(File.join(@basedir, @dirname))
@@ -27,6 +27,8 @@ class R10K::Environment::Git < R10K::Environment::WithModules
27
27
  # @return [R10K::Git::StatefulRepository] The git repo backing this environment
28
28
  attr_reader :repo
29
29
 
30
+ include R10K::Util::Setopts
31
+
30
32
  # Initialize the given Git environment.
31
33
  #
32
34
  # @param name [String] The unique name describing this environment.
@@ -38,8 +40,21 @@ class R10K::Environment::Git < R10K::Environment::WithModules
38
40
  # @param options [String] :ref The git reference to use for this environment
39
41
  def initialize(name, basedir, dirname, options = {})
40
42
  super
41
- @remote = options[:remote]
42
- @ref = options[:ref]
43
+ setopts(options, {
44
+ # Standard option interface
45
+ :version => :ref,
46
+ :source => :remote,
47
+ :type => ::R10K::Util::Setopts::Ignore,
48
+
49
+ # Type-specific options
50
+ :ref => :self,
51
+ :remote => :self,
52
+
53
+ }, raise_on_unhandled: false)
54
+ # TODO: in r10k 4.0.0, a major version bump, stop allowing garbage options.
55
+ # We only allow them now, here, on this object, because prior to adopting
56
+ # setopts in the constructor, this object type didn't do any validation
57
+ # checking of options passed, and would permit garbage parameters.
43
58
 
44
59
  @repo = R10K::Git::StatefulRepository.new(@remote, @basedir, @dirname)
45
60
  end
@@ -12,13 +12,13 @@ module R10K
12
12
  INVALID_CHARACTERS = %r[\W]
13
13
 
14
14
  def initialize(name, opts)
15
- @name = name
16
- @opts = opts
17
-
18
15
  @source = opts[:source]
19
16
  @prefix = opts[:prefix]
20
17
  @invalid = opts[:invalid]
21
18
 
19
+ @name = derive_name(name, opts[:strip_component])
20
+ @opts = opts
21
+
22
22
  case @invalid
23
23
  when 'correct_and_warn'
24
24
  @validate = true
@@ -71,8 +71,26 @@ module R10K
71
71
 
72
72
  private
73
73
 
74
- def derive_prefix(source,prefix)
74
+ def derive_name(name, strip_component)
75
+ return name unless strip_component
76
+
77
+ unless strip_component.is_a?(String)
78
+ raise _('Improper configuration value given for strip_component setting in %{src} source. ' \
79
+ 'Value must be a string, a /regex/, false, or omitted. Got "%{val}" (%{type})' \
80
+ % {src: @source, val: strip_component, type: strip_component.class})
81
+ end
75
82
 
83
+ if %r{^/.*/$}.match(strip_component)
84
+ regex = Regexp.new(strip_component[1..-2])
85
+ name.gsub(regex, '')
86
+ elsif name.start_with?(strip_component)
87
+ name[strip_component.size..-1]
88
+ else
89
+ name
90
+ end
91
+ end
92
+
93
+ def derive_prefix(source,prefix)
76
94
  if prefix == true
77
95
  "#{source}_"
78
96
  elsif prefix.is_a? String
@@ -44,8 +44,17 @@ class R10K::Environment::SVN < R10K::Environment::Base
44
44
  # @option options [String] :password The SVN password
45
45
  def initialize(name, basedir, dirname, options = {})
46
46
  super
47
+ setopts(options, {
48
+ # Standard option interface
49
+ :source => :remote,
50
+ :version => :expected_revision,
51
+ :type => ::R10K::Util::Setopts::Ignore,
47
52
 
48
- setopts(options, {:remote => :self, :username => :self, :password => :self, :puppetfile_name => :self })
53
+ # Type-specific options
54
+ :remote => :self,
55
+ :username => :self,
56
+ :password => :self,
57
+ })
49
58
 
50
59
  @working_dir = R10K::SVN::WorkingDir.new(Pathname.new(@full_path), :username => @username, :password => @password)
51
60
  end
@@ -61,7 +70,7 @@ class R10K::Environment::SVN < R10K::Environment::Base
61
70
  if @working_dir.is_svn?
62
71
  @working_dir.update
63
72
  else
64
- @working_dir.checkout(@remote)
73
+ @working_dir.checkout(@remote, @expected_revision)
65
74
  end
66
75
  @synced = true
67
76
  end