r10k 3.9.2 → 3.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) 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/deploy/environment.rb +17 -2
  17. data/lib/r10k/action/deploy/module.rb +38 -7
  18. data/lib/r10k/action/puppetfile/check.rb +7 -5
  19. data/lib/r10k/action/puppetfile/install.rb +22 -16
  20. data/lib/r10k/action/puppetfile/purge.rb +12 -9
  21. data/lib/r10k/action/runner.rb +45 -10
  22. data/lib/r10k/cli/deploy.rb +5 -0
  23. data/lib/r10k/cli/puppetfile.rb +0 -1
  24. data/lib/r10k/content_synchronizer.rb +16 -4
  25. data/lib/r10k/environment/base.rb +64 -11
  26. data/lib/r10k/environment/with_modules.rb +6 -10
  27. data/lib/r10k/git/cache.rb +1 -1
  28. data/lib/r10k/git/rugged/credentials.rb +77 -0
  29. data/lib/r10k/git/stateful_repository.rb +8 -0
  30. data/lib/r10k/git.rb +3 -0
  31. data/lib/r10k/initializers.rb +4 -0
  32. data/lib/r10k/module/base.rb +42 -1
  33. data/lib/r10k/module/definition.rb +64 -0
  34. data/lib/r10k/module/forge.rb +17 -4
  35. data/lib/r10k/module/git.rb +24 -2
  36. data/lib/r10k/module/local.rb +2 -3
  37. data/lib/r10k/module/svn.rb +12 -1
  38. data/lib/r10k/module.rb +20 -2
  39. data/lib/r10k/module_loader/puppetfile/dsl.rb +42 -0
  40. data/lib/r10k/module_loader/puppetfile.rb +272 -0
  41. data/lib/r10k/puppetfile.rb +82 -160
  42. data/lib/r10k/settings/definition.rb +1 -1
  43. data/lib/r10k/settings.rb +58 -2
  44. data/lib/r10k/source/base.rb +10 -0
  45. data/lib/r10k/source/git.rb +5 -0
  46. data/lib/r10k/source/svn.rb +4 -0
  47. data/lib/r10k/util/purgeable.rb +70 -8
  48. data/lib/r10k/version.rb +1 -1
  49. data/locales/r10k.pot +165 -65
  50. data/r10k.gemspec +2 -0
  51. data/spec/fixtures/unit/action/r10k_forge_auth.yaml +4 -0
  52. data/spec/fixtures/unit/action/r10k_forge_auth_no_url.yaml +3 -0
  53. data/spec/fixtures/unit/puppetfile/forge-override/Puppetfile +8 -0
  54. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile +9 -0
  55. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile.new +9 -0
  56. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_subdir_2/ignored_1 +0 -0
  57. data/spec/fixtures/unit/util/purgeable/managed_two/.hidden/unmanaged_3 +0 -0
  58. data/spec/r10k-mocks/mock_env.rb +3 -0
  59. data/spec/r10k-mocks/mock_source.rb +7 -3
  60. data/spec/unit/action/deploy/environment_spec.rb +105 -30
  61. data/spec/unit/action/deploy/module_spec.rb +232 -42
  62. data/spec/unit/action/puppetfile/check_spec.rb +17 -5
  63. data/spec/unit/action/puppetfile/install_spec.rb +42 -36
  64. data/spec/unit/action/puppetfile/purge_spec.rb +15 -17
  65. data/spec/unit/action/runner_spec.rb +122 -26
  66. data/spec/unit/environment/base_spec.rb +30 -17
  67. data/spec/unit/environment/git_spec.rb +2 -2
  68. data/spec/unit/environment/svn_spec.rb +4 -3
  69. data/spec/unit/environment/with_modules_spec.rb +2 -1
  70. data/spec/unit/git/cache_spec.rb +14 -0
  71. data/spec/unit/git/rugged/credentials_spec.rb +29 -0
  72. data/spec/unit/git/stateful_repository_spec.rb +5 -0
  73. data/spec/unit/module/base_spec.rb +54 -8
  74. data/spec/unit/module/forge_spec.rb +59 -5
  75. data/spec/unit/module/git_spec.rb +67 -17
  76. data/spec/unit/module/svn_spec.rb +35 -5
  77. data/spec/unit/module_loader/puppetfile_spec.rb +403 -0
  78. data/spec/unit/module_spec.rb +28 -0
  79. data/spec/unit/puppetfile_spec.rb +125 -189
  80. data/spec/unit/settings_spec.rb +47 -2
  81. data/spec/unit/util/purgeable_spec.rb +38 -6
  82. metadata +28 -2
@@ -4,10 +4,17 @@ require 'r10k/module'
4
4
  require 'r10k/util/purgeable'
5
5
  require 'r10k/errors'
6
6
  require 'r10k/content_synchronizer'
7
+ require 'r10k/module_loader/puppetfile/dsl'
8
+ require 'r10k/module_loader/puppetfile'
7
9
 
8
10
  module R10K
11
+
12
+ # Deprecated, use R10K::ModuleLoader::Puppetfile#load to load content,
13
+ # provide the `:modules` key of the returned Hash to
14
+ # R10K::ContentSynchronizer (either the `serial_sync` or `concurrent_sync`)
15
+ # and the remaining keys (`:managed_directories`, `:desired_contents`, and
16
+ # `:purge_exclusions`) to R10K::Util::Cleaner.
9
17
  class Puppetfile
10
- # Defines the data members of a Puppetfile
11
18
 
12
19
  include R10K::Settings::Mixin
13
20
 
@@ -19,25 +26,13 @@ class Puppetfile
19
26
  # @return [String] The URL to use for the Puppet Forge
20
27
  attr_reader :forge
21
28
 
22
- # @!attribute [r] modules
23
- # @return [Array<R10K::Module>]
24
- attr_reader :modules
25
-
26
29
  # @!attribute [r] basedir
27
30
  # @return [String] The base directory that contains the Puppetfile
28
31
  attr_reader :basedir
29
32
 
30
- # @!attribute [r] moduledir
31
- # @return [String] The directory to install the modules #{basedir}/modules
32
- attr_reader :moduledir
33
-
34
- # @!attrbute [r] puppetfile_path
35
- # @return [String] The path to the Puppetfile
36
- attr_reader :puppetfile_path
37
-
38
- # @!attribute [rw] environment
33
+ # @!attribute [r] environment
39
34
  # @return [R10K::Environment] Optional R10K::Environment that this Puppetfile belongs to.
40
- attr_accessor :environment
35
+ attr_reader :environment
41
36
 
42
37
  # @!attribute [rw] force
43
38
  # @return [Boolean] Overwrite any locally made changes
@@ -47,6 +42,10 @@ class Puppetfile
47
42
  # @return [Hash] Various settings overridden from normal configs
48
43
  attr_reader :overrides
49
44
 
45
+ # @!attribute [r] loader
46
+ # @return [R10K::ModuleLoader::Puppetfile] The internal module loader
47
+ attr_reader :loader
48
+
50
49
  # @param [String] basedir
51
50
  # @param [Hash, String, nil] options_or_moduledir The directory to install the modules or a Hash of options.
52
51
  # Usage as moduledir is deprecated. Only use as options, defaults to nil
@@ -65,144 +64,122 @@ class Puppetfile
65
64
 
66
65
  @force = deprecated_force_arg || options.delete(:force) || false
67
66
  @moduledir = deprecated_moduledir_arg || options.delete(:moduledir) || File.join(basedir, 'modules')
68
- @puppetfile_name = deprecated_name_arg || options.delete(:puppetfile_name) || 'Puppetfile'
69
- @puppetfile_path = deprecated_path_arg || options.delete(:puppetfile_path) || File.join(basedir, @puppetfile_name)
67
+ puppetfile_name = deprecated_name_arg || options.delete(:puppetfile_name) || 'Puppetfile'
68
+ puppetfile_path = deprecated_path_arg || options.delete(:puppetfile_path)
69
+ @puppetfile = puppetfile_path || puppetfile_name
70
+ @environment = options.delete(:environment)
70
71
 
71
72
  @overrides = options.delete(:overrides) || {}
73
+ @default_branch_override = @overrides.dig(:environments, :default_branch_override)
72
74
 
73
- logger.info _("Using Puppetfile '%{puppetfile}'") % {puppetfile: @puppetfile_path}
74
-
75
- @modules = []
76
- @managed_content = {}
77
75
  @forge = 'forgeapi.puppetlabs.com'
78
76
 
77
+ @loader = ::R10K::ModuleLoader::Puppetfile.new(
78
+ basedir: @basedir,
79
+ moduledir: @moduledir,
80
+ puppetfile: @puppetfile,
81
+ overrides: @overrides,
82
+ environment: @environment
83
+ )
84
+
85
+ @loaded_content = {
86
+ modules: [],
87
+ managed_directories: [],
88
+ desired_contents: [],
89
+ purge_exclusions: []
90
+ }
91
+
79
92
  @loaded = false
80
93
  end
81
94
 
95
+ # @param [String] default_branch_override The default branch to use
96
+ # instead of one specified in the module declaration, if applicable.
97
+ # Deprecated, use R10K::ModuleLoader::Puppetfile directly and pass
98
+ # the default_branch_override as an option on initialization.
82
99
  def load(default_branch_override = nil)
83
- return true if self.loaded?
84
- if File.readable? @puppetfile_path
85
- self.load!(default_branch_override)
100
+ if self.loaded?
101
+ return @loaded_content
86
102
  else
87
- logger.debug _("Puppetfile %{path} missing or unreadable") % {path: @puppetfile_path.inspect}
103
+ if !File.readable?(puppetfile_path)
104
+ logger.debug _("Puppetfile %{path} missing or unreadable") % {path: puppetfile_path.inspect}
105
+ else
106
+ self.load!(default_branch_override)
107
+ end
88
108
  end
89
109
  end
90
110
 
111
+ # @param [String] default_branch_override The default branch to use
112
+ # instead of one specified in the module declaration, if applicable.
113
+ # Deprecated, use R10K::ModuleLoader::Puppetfile directly and pass
114
+ # the default_branch_override as an option on initialization.
91
115
  def load!(default_branch_override = nil)
92
- @default_branch_override = default_branch_override
93
116
 
94
- dsl = R10K::Puppetfile::DSL.new(self)
95
- dsl.instance_eval(puppetfile_contents, @puppetfile_path)
117
+ if default_branch_override && (default_branch_override != @default_branch_override)
118
+ logger.warn("Mismatch between passed and initialized default branch overrides, preferring passed value.")
119
+ @loader.default_branch_override = default_branch_override
120
+ end
96
121
 
97
- validate_no_duplicate_names(@modules)
122
+ @loaded_content = @loader.load!
98
123
  @loaded = true
99
- rescue SyntaxError, LoadError, ArgumentError, NameError => e
100
- raise R10K::Error.wrap(e, _("Failed to evaluate %{path}") % {path: @puppetfile_path})
124
+
125
+ @loaded_content
101
126
  end
102
127
 
103
128
  def loaded?
104
129
  @loaded
105
130
  end
106
131
 
107
- # @param [Array<String>] modules
108
- def validate_no_duplicate_names(modules)
109
- dupes = modules
110
- .group_by { |mod| mod.name }
111
- .select { |_, v| v.size > 1 }
112
- .map(&:first)
113
- unless dupes.empty?
114
- msg = _('Puppetfiles cannot contain duplicate module names.')
115
- msg += ' '
116
- msg += _("Remove the duplicates of the following modules: %{dupes}" % { dupes: dupes.join(' ') })
117
- raise R10K::Error.new(msg)
118
- end
132
+ def modules
133
+ @loaded_content[:modules]
119
134
  end
120
135
 
121
- # @param [String] forge
122
- def set_forge(forge)
123
- @forge = forge
136
+ # @see R10K::ModuleLoader::Puppetfile#add_module for upcoming signature changes
137
+ def add_module(name, args)
138
+ @loader.add_module(name, args)
124
139
  end
125
140
 
126
- # @param [String] moduledir
127
- def set_moduledir(moduledir)
128
- @moduledir = if Pathname.new(moduledir).absolute?
129
- moduledir
130
- else
131
- File.join(basedir, moduledir)
132
- end
141
+ def set_moduledir(dir)
142
+ @loader.set_moduledir(dir)
133
143
  end
134
144
 
135
- # @param [String] name
136
- # @param [Hash, String, Symbol] args Calling with anything but a Hash is
137
- # deprecated. The DSL will now convert String and Symbol versions to
138
- # Hashes of the shape
139
- # { version: <String or Symbol> }
140
- #
141
- def add_module(name, args)
142
- if !args.is_a?(Hash)
143
- args = { version: args }
144
- end
145
-
146
- args[:overrides] = @overrides
147
-
148
- if install_path = args.delete(:install_path)
149
- install_path = resolve_install_path(install_path)
150
- validate_install_path(install_path, name)
151
- else
152
- install_path = @moduledir
153
- end
154
-
155
- if @default_branch_override != nil
156
- args[:default_branch_override] = @default_branch_override
157
- end
158
-
159
-
160
- mod = R10K::Module.new(name, install_path, args, @environment)
161
- mod.origin = :puppetfile
145
+ def set_forge(forge)
146
+ @loader.set_forge(forge)
147
+ end
162
148
 
163
- # Do not load modules if they would conflict with the attached
164
- # environment
165
- if environment && environment.module_conflicts?(mod)
166
- mod = nil
167
- return @modules
168
- end
149
+ def moduledir
150
+ @loader.moduledir
151
+ end
169
152
 
170
- # Keep track of all the content this Puppetfile is managing to enable purging.
171
- @managed_content[install_path] = Array.new unless @managed_content.has_key?(install_path)
172
- @managed_content[install_path] << mod.name
153
+ def puppetfile_path
154
+ @loader.puppetfile_path
155
+ end
173
156
 
174
- @modules << mod
157
+ def environment=(env)
158
+ @loader.environment = env
159
+ @environment = env
175
160
  end
176
161
 
177
162
  include R10K::Util::Purgeable
178
163
 
179
164
  def managed_directories
180
- self.load unless @loaded
165
+ self.load
181
166
 
182
- dirs = @managed_content.keys
183
- dirs.delete(real_basedir)
184
- dirs
167
+ @loaded_content[:managed_directories]
185
168
  end
186
169
 
187
170
  # Returns an array of the full paths to all the content being managed.
188
171
  # @note This implements a required method for the Purgeable mixin
189
172
  # @return [Array<String>]
190
173
  def desired_contents
191
- self.load unless @loaded
174
+ self.load
192
175
 
193
- @managed_content.flat_map do |install_path, modnames|
194
- modnames.collect { |name| File.join(install_path, name) }
195
- end
176
+ @loaded_content[:desired_contents]
196
177
  end
197
178
 
198
179
  def purge_exclusions
199
- exclusions = managed_directories
200
-
201
- if environment && environment.respond_to?(:desired_contents)
202
- exclusions += environment.desired_contents
203
- end
180
+ self.load
204
181
 
205
- exclusions
182
+ @loaded_content[:purge_exclusions]
206
183
  end
207
184
 
208
185
  def accept(visitor)
@@ -225,65 +202,10 @@ class Puppetfile
225
202
 
226
203
  private
227
204
 
228
- def puppetfile_contents
229
- File.read(@puppetfile_path)
230
- end
231
-
232
- def resolve_install_path(path)
233
- pn = Pathname.new(path)
234
-
235
- unless pn.absolute?
236
- pn = Pathname.new(File.join(basedir, path))
237
- end
238
-
239
- # .cleanpath is as good as we can do without touching the filesystem.
240
- # The .realpath methods will also choke if some of the intermediate
241
- # paths are missing, even though we will create them later as needed.
242
- pn.cleanpath.to_s
243
- end
244
-
245
- def validate_install_path(path, modname)
246
- unless /^#{Regexp.escape(real_basedir)}.*/ =~ path
247
- raise R10K::Error.new("Puppetfile cannot manage content '#{modname}' outside of containing environment: #{path} is not within #{real_basedir}")
248
- end
249
-
250
- true
251
- end
252
-
253
205
  def real_basedir
254
206
  Pathname.new(basedir).cleanpath.to_s
255
207
  end
256
208
 
257
- class DSL
258
- # A barebones implementation of the Puppetfile DSL
259
- #
260
- # @api private
261
-
262
- def initialize(librarian)
263
- @librarian = librarian
264
- end
265
-
266
- def mod(name, args = nil)
267
- if args.is_a?(Hash)
268
- opts = args
269
- else
270
- opts = { version: args }
271
- end
272
-
273
- @librarian.add_module(name, opts)
274
- end
275
-
276
- def forge(location)
277
- @librarian.set_forge(location)
278
- end
279
-
280
- def moduledir(location)
281
- @librarian.set_moduledir(location)
282
- end
283
-
284
- def method_missing(method, *args)
285
- raise NoMethodError, _("unrecognized declaration '%{method}'") % {method: method}
286
- end
287
- end
209
+ DSL = R10K::ModuleLoader::Puppetfile::DSL
288
210
  end
289
211
  end
@@ -90,7 +90,7 @@ module R10K
90
90
  def resolve
91
91
  if !@value.nil?
92
92
  @value
93
- elsif @default
93
+ elsif !@default.nil?
94
94
  if @default == :inherit
95
95
  # walk all the way up to root, starting with grandparent
96
96
  ancestor = parent
data/lib/r10k/settings.rb CHANGED
@@ -42,6 +42,22 @@ module R10K
42
42
  Only used by the 'rugged' Git provider."
43
43
  }),
44
44
 
45
+ Definition.new(:github_app_id, {
46
+ :desc => "The Github App id for Git SSL remotes.
47
+ Only used by the 'rugged' Git provider."
48
+ }),
49
+
50
+ Definition.new(:github_app_key, {
51
+ :desc => "The Github App private key for Git SSL remotes.
52
+ Only used by the 'rugged' Git provider."
53
+ }),
54
+
55
+ Definition.new(:github_app_ttl, {
56
+ :desc => "The ttl expiration for SSL tokens.
57
+ Only used by the 'rugged' Git provider.",
58
+ :default => "120",
59
+ }),
60
+
45
61
  URIDefinition.new(:proxy, {
46
62
  :desc => "An optional proxy server to use when interacting with Git sources via HTTP(S).",
47
63
  :default => :inherit,
@@ -65,6 +81,24 @@ module R10K
65
81
  :default => :inherit
66
82
  }),
67
83
 
84
+ Definition.new(:github_app_id, {
85
+ :desc => "The Github App id for Git SSL remotes.
86
+ Only used by the 'rugged' Git provider.",
87
+ :default => :inherit
88
+ }),
89
+
90
+ Definition.new(:github_app_key, {
91
+ :desc => "The Github App private key for Git SSL remotes.
92
+ Only used by the 'rugged' Git provider.",
93
+ :default => :inherit
94
+ }),
95
+
96
+ Definition.new(:github_app_ttl, {
97
+ :desc => "The ttl expiration for Git SSL tokens.
98
+ Only used by the 'rugged' Git provider.",
99
+ :default => :inherit
100
+ }),
101
+
68
102
  URIDefinition.new(:proxy, {
69
103
  :desc => "An optional proxy server to use when interacting with Git sources via HTTP(S).",
70
104
  :default => :inherit,
@@ -92,6 +126,20 @@ module R10K
92
126
  URIDefinition.new(:baseurl, {
93
127
  :desc => "The URL to the Puppet Forge to use for downloading modules."
94
128
  }),
129
+
130
+ Definition.new(:authorization_token, {
131
+ :desc => "The token for Puppet Forge authorization. Leave blank for unauthorized or license-based connections."
132
+ }),
133
+
134
+ Definition.new(:allow_puppetfile_override, {
135
+ :desc => "Whether to use `forge` declarations in the Puppetfile as an override of `baseurl`.",
136
+ :default => false,
137
+ :validate => lambda do |value|
138
+ unless !!value == value
139
+ raise ArgumentError, "`allow_puppetfile_override` can only be a boolean value, not '#{value}'"
140
+ end
141
+ end
142
+ })
95
143
  ])
96
144
  end
97
145
 
@@ -158,7 +206,15 @@ module R10K
158
206
  end
159
207
  end
160
208
  }),
161
- ])
209
+ Definition.new(:exclude_spec, {
210
+ :desc => "Whether or not to deploy the spec dir of a module. Defaults to false.",
211
+ :default => false,
212
+ :validate => lambda do |value|
213
+ unless !!value == value
214
+ raise ArgumentError, "`exclude_spec` can only be a boolean value, not '#{value}'"
215
+ end
216
+ end
217
+ })])
162
218
  end
163
219
 
164
220
  def self.global_settings
@@ -177,7 +233,7 @@ module R10K
177
233
  }),
178
234
 
179
235
  Definition.new(:postrun, {
180
- :desc => "The command r10k should run after deploying environments.",
236
+ :desc => "The command r10k should run after deploying environments or modules.",
181
237
  :validate => lambda do |value|
182
238
  if !value.is_a?(Array)
183
239
  raise ArgumentError, "The postrun setting should be an array of strings, not a #{value.class}"
@@ -63,6 +63,16 @@ class R10K::Source::Base
63
63
 
64
64
  end
65
65
 
66
+ # Perform actions to reload environments after the `preload!`. Similar
67
+ # to preload!, and likely to include network queries and rerunning
68
+ # environment generation.
69
+ #
70
+ # @api public
71
+ # @abstract
72
+ # @return [void]
73
+ def reload!
74
+ end
75
+
66
76
  # Enumerate the environments associated with this SVN source.
67
77
  #
68
78
  # @api public