r10k 3.9.3 → 3.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec_tests.yml +1 -1
  3. data/.travis.yml +0 -10
  4. data/CHANGELOG.mkd +33 -0
  5. data/README.mkd +6 -0
  6. data/doc/dynamic-environments/configuration.mkd +25 -0
  7. data/doc/dynamic-environments/usage.mkd +26 -0
  8. data/doc/puppetfile.mkd +18 -5
  9. data/integration/Rakefile +3 -1
  10. data/integration/tests/basic_functionality/basic_deployment.rb +176 -0
  11. data/integration/tests/user_scenario/basic_workflow/negative/neg_specify_deleted_forge_module.rb +3 -9
  12. data/integration/tests/user_scenario/basic_workflow/single_env_purge_unmanaged_modules.rb +21 -25
  13. data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +3 -3
  14. data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +3 -3
  15. data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +3 -3
  16. data/lib/r10k/action/base.rb +1 -1
  17. data/lib/r10k/action/deploy/deploy_helpers.rb +4 -0
  18. data/lib/r10k/action/deploy/display.rb +1 -1
  19. data/lib/r10k/action/deploy/environment.rb +22 -9
  20. data/lib/r10k/action/deploy/module.rb +41 -11
  21. data/lib/r10k/action/puppetfile/check.rb +7 -5
  22. data/lib/r10k/action/puppetfile/install.rb +22 -16
  23. data/lib/r10k/action/puppetfile/purge.rb +12 -9
  24. data/lib/r10k/action/runner.rb +45 -10
  25. data/lib/r10k/action/visitor.rb +3 -0
  26. data/lib/r10k/cli/deploy.rb +5 -0
  27. data/lib/r10k/cli/puppetfile.rb +0 -1
  28. data/lib/r10k/content_synchronizer.rb +16 -4
  29. data/lib/r10k/environment/base.rb +64 -11
  30. data/lib/r10k/environment/with_modules.rb +6 -10
  31. data/lib/r10k/git/cache.rb +1 -1
  32. data/lib/r10k/git/rugged/credentials.rb +77 -0
  33. data/lib/r10k/git/stateful_repository.rb +8 -0
  34. data/lib/r10k/git.rb +3 -0
  35. data/lib/r10k/initializers.rb +4 -0
  36. data/lib/r10k/module/base.rb +42 -1
  37. data/lib/r10k/module/definition.rb +64 -0
  38. data/lib/r10k/module/forge.rb +16 -3
  39. data/lib/r10k/module/git.rb +23 -1
  40. data/lib/r10k/module/local.rb +6 -3
  41. data/lib/r10k/module/svn.rb +11 -0
  42. data/lib/r10k/module.rb +20 -2
  43. data/lib/r10k/module_loader/puppetfile/dsl.rb +42 -0
  44. data/lib/r10k/module_loader/puppetfile.rb +276 -0
  45. data/lib/r10k/puppetfile.rb +82 -160
  46. data/lib/r10k/settings/definition.rb +1 -1
  47. data/lib/r10k/settings.rb +58 -2
  48. data/lib/r10k/source/base.rb +10 -0
  49. data/lib/r10k/source/git.rb +5 -0
  50. data/lib/r10k/source/svn.rb +4 -0
  51. data/lib/r10k/util/purgeable.rb +74 -8
  52. data/lib/r10k/util/setopts.rb +2 -0
  53. data/lib/r10k/util/subprocess.rb +1 -0
  54. data/lib/r10k/version.rb +1 -1
  55. data/locales/r10k.pot +165 -65
  56. data/r10k.gemspec +2 -0
  57. data/spec/fixtures/unit/action/r10k_forge_auth.yaml +4 -0
  58. data/spec/fixtures/unit/action/r10k_forge_auth_no_url.yaml +3 -0
  59. data/spec/fixtures/unit/puppetfile/forge-override/Puppetfile +8 -0
  60. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile +10 -0
  61. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile.new +10 -0
  62. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_subdir_2/ignored_1 +0 -0
  63. data/spec/fixtures/unit/util/purgeable/managed_two/.hidden/unmanaged_3 +0 -0
  64. data/spec/r10k-mocks/mock_env.rb +3 -0
  65. data/spec/r10k-mocks/mock_source.rb +7 -3
  66. data/spec/unit/action/deploy/environment_spec.rb +105 -30
  67. data/spec/unit/action/deploy/module_spec.rb +232 -42
  68. data/spec/unit/action/puppetfile/check_spec.rb +17 -5
  69. data/spec/unit/action/puppetfile/install_spec.rb +42 -36
  70. data/spec/unit/action/puppetfile/purge_spec.rb +15 -17
  71. data/spec/unit/action/runner_spec.rb +122 -26
  72. data/spec/unit/environment/base_spec.rb +30 -17
  73. data/spec/unit/environment/git_spec.rb +2 -2
  74. data/spec/unit/environment/svn_spec.rb +4 -3
  75. data/spec/unit/environment/with_modules_spec.rb +2 -1
  76. data/spec/unit/git/cache_spec.rb +14 -0
  77. data/spec/unit/git/rugged/credentials_spec.rb +29 -0
  78. data/spec/unit/git/stateful_repository_spec.rb +5 -0
  79. data/spec/unit/module/base_spec.rb +54 -8
  80. data/spec/unit/module/forge_spec.rb +59 -5
  81. data/spec/unit/module/git_spec.rb +67 -9
  82. data/spec/unit/module/svn_spec.rb +35 -5
  83. data/spec/unit/module_loader/puppetfile_spec.rb +405 -0
  84. data/spec/unit/module_spec.rb +12 -1
  85. data/spec/unit/puppetfile_spec.rb +125 -189
  86. data/spec/unit/settings_spec.rb +47 -2
  87. data/spec/unit/util/purgeable_spec.rb +38 -6
  88. metadata +29 -3
@@ -1,10 +1,9 @@
1
- require 'r10k/util/setopts'
2
- require 'r10k/util/cleaner'
3
- require 'r10k/deployment'
4
- require 'r10k/logging'
5
- require 'r10k/action/visitor'
6
1
  require 'r10k/action/base'
7
2
  require 'r10k/action/deploy/deploy_helpers'
3
+ require 'r10k/action/visitor'
4
+ require 'r10k/deployment'
5
+ require 'r10k/util/setopts'
6
+
8
7
  require 'json'
9
8
 
10
9
  module R10K
@@ -13,6 +12,7 @@ module R10K
13
12
  class Environment < R10K::Action::Base
14
13
 
15
14
  include R10K::Action::Deploy::DeployHelpers
15
+ include R10K::Action::Visitor
16
16
 
17
17
  # Deprecated
18
18
  attr_reader :force
@@ -41,11 +41,14 @@ module R10K
41
41
  requested_environments: @argv.map { |arg| arg.gsub(/\W/,'_') },
42
42
  default_branch_override: @default_branch_override,
43
43
  generate_types: @generate_types || settings.dig(:deploy, :generate_types) || false,
44
- preload_environments: true
44
+ preload_environments: true,
45
+ incremental: @incremental
45
46
  },
46
47
  modules: {
48
+ exclude_spec: settings.dig(:deploy, :exclude_spec),
47
49
  requested_modules: [],
48
50
  deploy_modules: @modules,
51
+ pool_size: @settings[:pool_size] || 4,
49
52
  force: !@no_force, # force here is used to make it easier to reason about
50
53
  },
51
54
  purging: {
@@ -53,6 +56,9 @@ module R10K
53
56
  purge_allowlist: read_purge_allowlist(settings.dig(:deploy, :purge_whitelist) || [],
54
57
  settings.dig(:deploy, :purge_allowlist) || [])
55
58
  },
59
+ forge: {
60
+ allow_puppetfile_override: settings.dig(:forge, :allow_puppetfile_override) || false
61
+ },
56
62
  output: {}
57
63
  }
58
64
  })
@@ -75,8 +81,6 @@ module R10K
75
81
  @visit_ok
76
82
  end
77
83
 
78
- include R10K::Action::Visitor
79
-
80
84
  private
81
85
 
82
86
  def read_purge_allowlist (whitelist, allowlist)
@@ -116,6 +120,9 @@ module R10K
116
120
 
117
121
  if @settings.dig(:overrides, :purging, :purge_levels).include?(:deployment)
118
122
  logger.debug("Purging unmanaged environments for deployment...")
123
+ deployment.sources.each do |source|
124
+ source.reload!
125
+ end
119
126
  deployment.purge!
120
127
  end
121
128
  ensure
@@ -126,6 +133,7 @@ module R10K
126
133
  envs.reject! { |e| !requested_envs.include?(e) } if requested_envs.any?
127
134
  postcmd = postcmd.map { |e| e.gsub('$modifiedenvs', envs.join(' ')) }
128
135
  end
136
+ logger.debug _("Executing postrun command.")
129
137
  subproc = R10K::Util::Subprocess.new(postcmd)
130
138
  subproc.logger = logger
131
139
  subproc.execute
@@ -233,13 +241,18 @@ module R10K
233
241
  super.merge(puppetfile: :modules,
234
242
  modules: :self,
235
243
  cachedir: :self,
244
+ incremental: :self,
236
245
  'no-force': :self,
246
+ 'exclude-spec': :self,
237
247
  'generate-types': :self,
238
248
  'puppet-path': :self,
239
249
  'puppet-conf': :self,
240
250
  'private-key': :self,
241
251
  'oauth-token': :self,
242
- 'default-branch-override': :self)
252
+ 'default-branch-override': :self,
253
+ 'github-app-id': :self,
254
+ 'github-app-key': :self,
255
+ 'github-app-ttl': :self)
243
256
  end
244
257
  end
245
258
  end
@@ -1,7 +1,7 @@
1
- require 'r10k/deployment'
2
- require 'r10k/action/visitor'
3
1
  require 'r10k/action/base'
4
2
  require 'r10k/action/deploy/deploy_helpers'
3
+ require 'r10k/action/visitor'
4
+ require 'r10k/deployment'
5
5
 
6
6
  module R10K
7
7
  module Action
@@ -9,6 +9,7 @@ module R10K
9
9
  class Module < R10K::Action::Base
10
10
 
11
11
  include R10K::Action::Deploy::DeployHelpers
12
+ include R10K::Action::Visitor
12
13
 
13
14
  # Deprecated
14
15
  attr_reader :force
@@ -29,6 +30,7 @@ module R10K
29
30
  super
30
31
 
31
32
  requested_env = @opts[:environment] ? [@opts[:environment].gsub(/\W/, '_')] : []
33
+ @modified_envs = []
32
34
 
33
35
  @settings = @settings.merge({
34
36
  overrides: {
@@ -37,10 +39,15 @@ module R10K
37
39
  generate_types: @generate_types
38
40
  },
39
41
  modules: {
42
+ exclude_spec: settings.dig(:deploy, :exclude_spec),
43
+ pool_size: @settings[:pool_size] || 4,
40
44
  requested_modules: @argv.map.to_a,
41
45
  # force here is used to make it easier to reason about
42
46
  force: !@no_force
43
47
  },
48
+ forge: {
49
+ allow_puppetfile_override: settings.dig(:forge, :allow_puppetfile_override) || false
50
+ },
44
51
  purging: {},
45
52
  output: {}
46
53
  }
@@ -63,12 +70,27 @@ module R10K
63
70
  @visit_ok
64
71
  end
65
72
 
66
- include R10K::Action::Visitor
67
-
68
73
  private
69
74
 
70
75
  def visit_deployment(deployment)
71
76
  yield
77
+ ensure
78
+ if (postcmd = @settings[:postrun])
79
+ if @modified_envs.any?
80
+ envs_to_run = @modified_envs.join(' ')
81
+ logger.debug _("Running postrun command for environments: %{envs_to_run}.") % { envs_to_run: envs_to_run }
82
+
83
+ if postcmd.grep('$modifiedenvs').any?
84
+ postcmd = postcmd.map { |e| e.gsub('$modifiedenvs', envs_to_run) }
85
+ end
86
+
87
+ subproc = R10K::Util::Subprocess.new(postcmd)
88
+ subproc.logger = logger
89
+ subproc.execute
90
+ else
91
+ logger.debug _("No environments were modified, not executing postrun command.")
92
+ end
93
+ end
72
94
  end
73
95
 
74
96
  def visit_source(source)
@@ -82,13 +104,17 @@ module R10K
82
104
  else
83
105
  logger.debug1(_("Updating modules %{modules} in environment %{env_path}") % {modules: @settings.dig(:overrides, :modules, :requested_modules).inspect, env_path: environment.path})
84
106
 
85
- environment.deploy
107
+ updated_modules = environment.deploy
108
+
109
+ # We actually synced a module in this env
110
+ if !updated_modules.nil? && !updated_modules.empty?
111
+ # Record modified environment for postrun command
112
+ @modified_envs << environment.dirname
86
113
 
87
- requested_mods = @settings.dig(:overrides, :modules, :requested_modules) || []
88
- generate_types = @settings.dig(:overrides, :environments, :generate_types)
89
- if generate_types && !((environment.modules.map(&:name) & requested_mods).empty?)
90
- logger.debug("Generating puppet types for environment '#{environment.dirname}'...")
91
- environment.generate_types!
114
+ if generate_types = @settings.dig(:overrides, :environments, :generate_types)
115
+ logger.debug("Generating puppet types for environment '#{environment.dirname}'...")
116
+ environment.generate_types!
117
+ end
92
118
  end
93
119
  end
94
120
  end
@@ -96,12 +122,16 @@ module R10K
96
122
  def allowed_initialize_opts
97
123
  super.merge(environment: true,
98
124
  cachedir: :self,
125
+ 'exclude-spec': :self,
99
126
  'no-force': :self,
100
127
  'generate-types': :self,
101
128
  'puppet-path': :self,
102
129
  'puppet-conf': :self,
103
130
  'private-key': :self,
104
- 'oauth-token': :self)
131
+ 'oauth-token': :self,
132
+ 'github-app-id': :self,
133
+ 'github-app-key': :self,
134
+ 'github-app-ttl': :self)
105
135
  end
106
136
  end
107
137
  end
@@ -1,6 +1,6 @@
1
- require 'r10k/puppetfile'
2
1
  require 'r10k/action/base'
3
2
  require 'r10k/errors/formatting'
3
+ require 'r10k/module_loader/puppetfile'
4
4
 
5
5
  module R10K
6
6
  module Action
@@ -8,11 +8,13 @@ module R10K
8
8
  class Check < R10K::Action::Base
9
9
 
10
10
  def call
11
- pf = R10K::Puppetfile.new(@root,
12
- {moduledir: @moduledir,
13
- puppetfile_path: @puppetfile})
11
+ options = { basedir: @root }
12
+ options[:moduledir] = @moduledir if @moduledir
13
+ options[:puppetfile] = @puppetfile if @puppetfile
14
+
15
+ loader = R10K::ModuleLoader::Puppetfile.new(**options)
14
16
  begin
15
- pf.load!
17
+ loader.load!
16
18
  $stderr.puts _("Syntax OK")
17
19
  true
18
20
  rescue => e
@@ -1,7 +1,7 @@
1
- require 'r10k/puppetfile'
2
- require 'r10k/errors/formatting'
3
- require 'r10k/action/visitor'
4
1
  require 'r10k/action/base'
2
+ require 'r10k/content_synchronizer'
3
+ require 'r10k/errors/formatting'
4
+ require 'r10k/module_loader/puppetfile'
5
5
  require 'r10k/util/cleaner'
6
6
 
7
7
  module R10K
@@ -10,25 +10,31 @@ module R10K
10
10
  class Install < R10K::Action::Base
11
11
 
12
12
  def call
13
- @ok = true
14
13
  begin
15
- pf = R10K::Puppetfile.new(@root,
16
- {moduledir: @moduledir,
17
- puppetfile_path: @puppetfile,
18
- force: @force || false})
19
- pf.load!
20
- pf.sync
14
+ options = { basedir: @root, overrides: { force: @force || false } }
15
+ options[:moduledir] = @moduledir if @moduledir
16
+ options[:puppetfile] = @puppetfile if @puppetfile
17
+
18
+ loader = R10K::ModuleLoader::Puppetfile.new(**options)
19
+ loaded_content = loader.load!
21
20
 
22
- R10K::Util::Cleaner.new(pf.managed_directories,
23
- pf.desired_contents,
24
- pf.purge_exclusions).purge!
21
+ pool_size = @settings[:pool_size] || 4
22
+ modules = loaded_content[:modules]
23
+ if pool_size > 1
24
+ R10K::ContentSynchronizer.concurrent_sync(modules, pool_size, logger)
25
+ else
26
+ R10K::ContentSynchronizer.serial_sync(modules, logger)
27
+ end
25
28
 
29
+ R10K::Util::Cleaner.new(loaded_content[:managed_directories],
30
+ loaded_content[:desired_contents],
31
+ loaded_content[:purge_exclusions]).purge!
32
+
33
+ true
26
34
  rescue => e
27
- @ok = false
28
35
  logger.error R10K::Errors::Formatting.format_exception(e, @trace)
36
+ false
29
37
  end
30
-
31
- @ok
32
38
  end
33
39
 
34
40
  private
@@ -1,7 +1,7 @@
1
- require 'r10k/puppetfile'
2
- require 'r10k/util/cleaner'
3
1
  require 'r10k/action/base'
4
2
  require 'r10k/errors/formatting'
3
+ require 'r10k/module_loader/puppetfile'
4
+ require 'r10k/util/cleaner'
5
5
 
6
6
  module R10K
7
7
  module Action
@@ -9,13 +9,16 @@ module R10K
9
9
  class Purge < R10K::Action::Base
10
10
 
11
11
  def call
12
- pf = R10K::Puppetfile.new(@root,
13
- {moduledir: @moduledir,
14
- puppetfile_path: @puppetfile})
15
- pf.load!
16
- R10K::Util::Cleaner.new(pf.managed_directories,
17
- pf.desired_contents,
18
- pf.purge_exclusions).purge!
12
+ options = { basedir: @root }
13
+
14
+ options[:moduledir] = @moduledir if @moduledir
15
+ options[:puppetfile] = @puppetfile if @puppetfile
16
+
17
+ loader = R10K::ModuleLoader::Puppetfile.new(**options)
18
+ loaded_content = loader.load!
19
+ R10K::Util::Cleaner.new(loaded_content[:managed_directories],
20
+ loaded_content[:desired_contents],
21
+ loaded_content[:purge_exclusions]).purge!
19
22
 
20
23
  true
21
24
  rescue => e
@@ -44,10 +44,13 @@ module R10K
44
44
 
45
45
  overrides = {}
46
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')
47
+ if @opts.key?(:'puppet-path') || @opts.key?(:'generate-types') || @opts.key?(:'exclude-spec') || @opts.key?(:'puppet-conf')
48
+ overrides[:deploy] = {}
49
+ overrides[:deploy][:puppet_path] = @opts[:'puppet-path'] if @opts.key?(:'puppet-path')
50
+ overrides[:deploy][:puppet_conf] = @opts[:'puppet-conf'] if @opts.key?(:'puppet-conf')
51
+ overrides[:deploy][:generate_types] = @opts[:'generate-types'] if @opts.key?(:'generate-types')
52
+ overrides[:deploy][:exclude_spec] = @opts[:'exclude-spec'] if @opts.key?(:'exclude-spec')
53
+ end
51
54
 
52
55
  with_overrides = config_settings.merge(overrides) do |key, oldval, newval|
53
56
  newval = oldval.merge(newval) if oldval.is_a? Hash
@@ -67,15 +70,20 @@ module R10K
67
70
  exit(8)
68
71
  end
69
72
 
73
+ # Set up authorization from license file if it wasn't
74
+ # already set via the config
70
75
  def setup_authorization
71
- begin
72
- license = R10K::Util::License.load
76
+ if PuppetForge::Connection.authorization.nil?
77
+ begin
78
+ license = R10K::Util::License.load
73
79
 
74
- if license.respond_to?(:authorization_token)
75
- PuppetForge::Connection.authorization = license.authorization_token
80
+ if license.respond_to?(:authorization_token)
81
+ logger.debug "Using token from license to connect to the Forge."
82
+ PuppetForge::Connection.authorization = license.authorization_token
83
+ end
84
+ rescue R10K::Error => e
85
+ logger.warn e.message
76
86
  end
77
- rescue R10K::Error => e
78
- logger.warn e.message
79
87
  end
80
88
  end
81
89
 
@@ -100,11 +108,26 @@ module R10K
100
108
  def add_credential_overrides(overrides)
101
109
  sshkey_path = @opts[:'private-key']
102
110
  token_path = @opts[:'oauth-token']
111
+ app_id = @opts[:'github-app-id']
112
+ app_private_key_path = @opts[:'github-app-key']
113
+ app_ttl = @opts[:'github-app-ttl']
103
114
 
104
115
  if sshkey_path && token_path
105
116
  raise R10K::Error, "Cannot specify both an SSH key and a token to use with this deploy."
106
117
  end
107
118
 
119
+ if sshkey_path && (app_private_key_path || app_id)
120
+ raise R10K::Error, "Cannot specify both an SSH key and an SSL key or Github App id to use with this deploy."
121
+ end
122
+
123
+ if token_path && (app_private_key_path || app_id)
124
+ raise R10K::Error, "Cannot specify both an OAuth token and an SSL key or Github App id to use with this deploy."
125
+ end
126
+
127
+ if app_id && ! app_private_key_path || app_private_key_path && ! app_id
128
+ raise R10K::Error, "Must specify both id and SSL private key to use Github App for this deploy."
129
+ end
130
+
108
131
  if sshkey_path
109
132
  overrides[:git] ||= {}
110
133
  overrides[:git][:private_key] = sshkey_path
@@ -121,6 +144,18 @@ module R10K
121
144
  repo[:oauth_token] = token_path
122
145
  end
123
146
  end
147
+ elsif app_id
148
+ overrides[:git] ||= {}
149
+ overrides[:git][:github_app_id] = app_id
150
+ overrides[:git][:github_app_key] = app_private_key_path
151
+ overrides[:git][:github_app_ttl] = app_ttl
152
+ if repo_settings = overrides[:git][:repositories]
153
+ repo_settings.each do |repo|
154
+ repo[:github_app_id] = app_id
155
+ repo[:github_app_key] = app_private_key_path
156
+ repo[:github_app_ttl] = app_ttl
157
+ end
158
+ end
124
159
  end
125
160
 
126
161
  overrides
@@ -1,4 +1,5 @@
1
1
  require 'r10k/errors/formatting'
2
+ require 'r10k/logging'
2
3
 
3
4
  module R10K
4
5
  module Action
@@ -13,6 +14,8 @@ module R10K
13
14
  # @api private
14
15
  module Visitor
15
16
 
17
+ include R10K::Logging
18
+
16
19
  # Dispatch to the type specific visitor method
17
20
  #
18
21
  # @param type [Symbol] The object type to dispatch for
@@ -24,6 +24,7 @@ module R10K::CLI
24
24
  option nil, :cachedir, 'Specify a cachedir, overriding the value in config', argument: :required
25
25
  flag nil, :'no-force', 'Prevent the overwriting of local module modifications'
26
26
  flag nil, :'generate-types', 'Run `puppet generate types` after updating an environment'
27
+ flag nil, :'exclude-spec', 'Exclude the module\'s spec dir from deployment'
27
28
  option nil, :'puppet-path', 'Path to puppet executable', argument: :required do |value, cmd|
28
29
  unless File.executable? value
29
30
  $stderr.puts "The specified puppet executable #{value} is not executable."
@@ -34,6 +35,9 @@ module R10K::CLI
34
35
  option nil, :'puppet-conf', 'Path to puppet.conf', argument: :required
35
36
  option nil, :'private-key', 'Path to SSH key to use when cloning. Only valid with rugged provider', argument: :required
36
37
  option nil, :'oauth-token', 'Path to OAuth token to use when cloning. Only valid with rugged provider', argument: :required
38
+ option nil, :'github-app-id', 'Github App id. Only valid with rugged provider', argument: :required
39
+ option nil, :'github-app-key', 'Github App private key. Only valid with rugged provider', argument: :required
40
+ option nil, :'github-app-ttl', 'Github App token expiration, in seconds. Only valid with rugged provider', default: "120", argument: :optional
37
41
 
38
42
  run do |opts, args, cmd|
39
43
  puts cmd.help(:verbose => opts[:verbose])
@@ -65,6 +69,7 @@ scheduled. On subsequent deployments, Puppetfile deployment will default to off.
65
69
 
66
70
  flag :p, :puppetfile, 'Deploy modules (deprecated, use -m)'
67
71
  flag :m, :modules, 'Deploy modules'
72
+ flag nil, :incremental, 'Used with the --modules flag, only update those modules whose definition has changed or whose definition allows the version to float'
68
73
  option nil, :'default-branch-override', 'Specify a branchname to override the default branch in the puppetfile',
69
74
  argument: :required
70
75
 
@@ -1,5 +1,4 @@
1
1
  require 'r10k/cli'
2
- require 'r10k/puppetfile'
3
2
  require 'r10k/action/puppetfile'
4
3
 
5
4
  require 'cri'
@@ -8,24 +8,31 @@ module R10K
8
8
  end
9
9
 
10
10
  def self.serial_sync(modules)
11
+ updated_modules = []
11
12
  modules.each do |mod|
12
- mod.sync
13
+ updated = mod.sync
14
+ updated_modules << mod.name if updated
13
15
  end
16
+ updated_modules
14
17
  end
15
18
 
19
+ # Returns a Queue of the names of modules actually updated
16
20
  def self.concurrent_accept(modules, visitor, loader, pool_size, logger)
17
21
  mods_queue = modules_visit_queue(modules, visitor, loader)
18
22
  sync_queue(mods_queue, pool_size, logger)
19
23
  end
20
24
 
25
+ # Returns a Queue of the names of modules actually updated
21
26
  def self.concurrent_sync(modules, pool_size, logger)
22
27
  mods_queue = modules_sync_queue(modules)
23
28
  sync_queue(mods_queue, pool_size, logger)
24
29
  end
25
30
 
31
+ # Returns a Queue of the names of modules actually updated
26
32
  def self.sync_queue(mods_queue, pool_size, logger)
27
33
  logger.debug _("Updating modules with %{pool_size} threads") % {pool_size: pool_size}
28
- thread_pool = pool_size.times.map { sync_thread(mods_queue, logger) }
34
+ updated_modules = Queue.new
35
+ thread_pool = pool_size.times.map { sync_thread(mods_queue, logger, updated_modules) }
29
36
  thread_exception = nil
30
37
 
31
38
  # If any threads raise an exception the deployment is considered a failure.
@@ -33,6 +40,8 @@ module R10K
33
40
  # current work, then re-raise the first exception caught.
34
41
  begin
35
42
  thread_pool.each(&:join)
43
+ # Return the list of all modules that were actually updated
44
+ updated_modules
36
45
  rescue => e
37
46
  logger.error _("Error during concurrent deploy of a module: %{message}") % {message: e.message}
38
47
  mods_queue.clear
@@ -65,11 +74,14 @@ module R10K
65
74
  modules_by_cachedir.values.each {|mods| queue << mods }
66
75
  end
67
76
 
68
- def self.sync_thread(mods_queue, logger)
77
+ def self.sync_thread(mods_queue, logger, updated_modules)
69
78
  Thread.new do
70
79
  begin
71
80
  while mods = mods_queue.pop(true) do
72
- mods.each { |mod| mod.sync }
81
+ mods.each do |mod|
82
+ updated = mod.sync
83
+ updated_modules << mod.name if updated
84
+ end
73
85
  end
74
86
  rescue ThreadError => e
75
87
  logger.debug _("Module thread %{id} exiting: %{message}") % {message: e.message, id: Thread.current.object_id}
@@ -1,5 +1,8 @@
1
- require 'r10k/util/subprocess'
1
+ require 'r10k/content_synchronizer'
2
2
  require 'r10k/logging'
3
+ require 'r10k/module_loader/puppetfile'
4
+ require 'r10k/util/cleaner'
5
+ require 'r10k/util/subprocess'
3
6
 
4
7
  # This class defines a common interface for environment implementations.
5
8
  #
@@ -34,6 +37,10 @@ class R10K::Environment::Base
34
37
  # @return [String] The puppetfile name (relative)
35
38
  attr_reader :puppetfile_name
36
39
 
40
+ attr_reader :managed_directories, :desired_contents
41
+
42
+ attr_reader :loader
43
+
37
44
  # Initialize the given environment.
38
45
  #
39
46
  # @param name [String] The unique name describing this environment.
@@ -57,6 +64,20 @@ class R10K::Environment::Base
57
64
  force: @overrides.dig(:modules, :force),
58
65
  puppetfile_name: @puppetfile_name})
59
66
  @puppetfile.environment = self
67
+
68
+ loader_options = { basedir: @full_path, overrides: @overrides, environment: self }
69
+ loader_options[:puppetfile] = @puppetfile_name if @puppetfile_name
70
+
71
+ @loader = R10K::ModuleLoader::Puppetfile.new(**loader_options)
72
+
73
+ if @overrides.dig(:environments, :incremental)
74
+ @loader.load_metadata
75
+ end
76
+
77
+ @base_modules = nil
78
+ @purge_exclusions = nil
79
+ @managed_directories = [ @full_path ]
80
+ @desired_contents = []
60
81
  end
61
82
 
62
83
  # Synchronize the given environment.
@@ -106,8 +127,11 @@ class R10K::Environment::Base
106
127
  # @return [Array<R10K::Module::Base>] All modules defined in the Puppetfile
107
128
  # associated with this environment.
108
129
  def modules
109
- @puppetfile.load
110
- @puppetfile.modules
130
+ if @base_modules.nil?
131
+ load_puppetfile_modules
132
+ end
133
+
134
+ @base_modules
111
135
  end
112
136
 
113
137
  # @return [Array<R10K::Module::Base>] Whether or not the given module
@@ -123,29 +147,50 @@ class R10K::Environment::Base
123
147
  end
124
148
  end
125
149
 
150
+
151
+ # Returns a Queue of the names of modules actually updated
126
152
  def deploy
127
- puppetfile.load(@overrides.dig(:environments, :default_branch_override))
153
+ if @base_modules.nil?
154
+ load_puppetfile_modules
155
+ end
128
156
 
129
- puppetfile.sync
157
+ if ! @base_modules.empty?
158
+ pool_size = @overrides.dig(:modules, :pool_size)
159
+ updated_modules = R10K::ContentSynchronizer.concurrent_sync(@base_modules, pool_size, logger)
160
+ end
130
161
 
131
162
  if (@overrides.dig(:purging, :purge_levels) || []).include?(:puppetfile)
132
163
  logger.debug("Purging unmanaged Puppetfile content for environment '#{dirname}'...")
133
- R10K::Util::Cleaner.new(puppetfile.managed_directories,
134
- puppetfile.desired_contents,
135
- puppetfile.purge_exclusions).purge!
164
+ @puppetfile_cleaner.purge!
136
165
  end
166
+
167
+ updated_modules
168
+ end
169
+
170
+ def load_puppetfile_modules
171
+ loaded_content = @loader.load
172
+ @base_modules = loaded_content[:modules]
173
+
174
+ @purge_exclusions = determine_purge_exclusions(loaded_content[:managed_directories],
175
+ loaded_content[:desired_contents])
176
+
177
+ @puppetfile_cleaner = R10K::Util::Cleaner.new(loaded_content[:managed_directories],
178
+ loaded_content[:desired_contents],
179
+ loaded_content[:purge_exclusions])
137
180
  end
138
181
 
139
182
  def whitelist(user_whitelist=[])
140
183
  user_whitelist.collect { |pattern| File.join(@full_path, pattern) }
141
184
  end
142
185
 
143
- def purge_exclusions
186
+ def determine_purge_exclusions(pf_managed_dirs = @puppetfile.managed_directories,
187
+ pf_desired_contents = @puppetfile.desired_contents)
188
+
144
189
  list = [File.join(@full_path, '.r10k-deploy.json')].to_set
145
190
 
146
- list += @puppetfile.managed_directories
191
+ list += pf_managed_dirs
147
192
 
148
- list += @puppetfile.desired_contents.flat_map do |item|
193
+ list += pf_desired_contents.flat_map do |item|
149
194
  desired_tree = []
150
195
 
151
196
  if File.directory?(item)
@@ -163,6 +208,14 @@ class R10K::Environment::Base
163
208
  list.to_a
164
209
  end
165
210
 
211
+ def purge_exclusions
212
+ if @purge_exclusions.nil?
213
+ load_puppetfile_modules
214
+ end
215
+
216
+ @purge_exclusions
217
+ end
218
+
166
219
  def generate_types!
167
220
  argv = [R10K::Settings.puppet_path, 'generate', 'types', '--environment', dirname, '--environmentpath', basedir, '--config', R10K::Settings.puppet_conf]
168
221
  subproc = R10K::Util::Subprocess.new(argv)