r10k 3.9.0 → 3.9.1
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.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +1 -1
- data/.github/workflows/stale.yml +19 -0
- data/CHANGELOG.mkd +5 -0
- data/doc/dynamic-environments/configuration.mkd +6 -6
- data/lib/r10k/action/base.rb +8 -1
- data/lib/r10k/action/deploy/display.rb +39 -9
- data/lib/r10k/action/deploy/environment.rb +63 -40
- data/lib/r10k/action/deploy/module.rb +47 -28
- data/lib/r10k/action/puppetfile/check.rb +3 -1
- data/lib/r10k/action/puppetfile/install.rb +20 -23
- data/lib/r10k/action/puppetfile/purge.rb +8 -2
- data/lib/r10k/content_synchronizer.rb +83 -0
- data/lib/r10k/deployment.rb +1 -1
- data/lib/r10k/environment/base.rb +21 -1
- data/lib/r10k/environment/git.rb +0 -3
- data/lib/r10k/environment/svn.rb +4 -6
- data/lib/r10k/environment/with_modules.rb +18 -10
- data/lib/r10k/module.rb +1 -1
- data/lib/r10k/module/base.rb +17 -1
- data/lib/r10k/module/forge.rb +24 -18
- data/lib/r10k/module/git.rb +22 -13
- data/lib/r10k/module/local.rb +1 -0
- data/lib/r10k/module/svn.rb +11 -8
- data/lib/r10k/puppetfile.rb +55 -70
- data/lib/r10k/source/base.rb +4 -0
- data/lib/r10k/source/git.rb +14 -6
- data/lib/r10k/source/hash.rb +1 -3
- data/lib/r10k/source/svn.rb +0 -2
- data/lib/r10k/util/cleaner.rb +21 -0
- data/lib/r10k/version.rb +1 -1
- data/locales/r10k.pot +51 -59
- data/spec/r10k-mocks/mock_source.rb +1 -1
- data/spec/shared-examples/puppetfile-action.rb +7 -7
- data/spec/unit/action/deploy/display_spec.rb +32 -6
- data/spec/unit/action/deploy/environment_spec.rb +76 -48
- data/spec/unit/action/deploy/module_spec.rb +139 -31
- data/spec/unit/action/puppetfile/check_spec.rb +2 -2
- data/spec/unit/action/puppetfile/install_spec.rb +31 -10
- data/spec/unit/action/puppetfile/purge_spec.rb +25 -5
- data/spec/unit/module/forge_spec.rb +15 -13
- data/spec/unit/module/git_spec.rb +8 -0
- data/spec/unit/module_spec.rb +5 -5
- data/spec/unit/puppetfile_spec.rb +40 -26
- data/spec/unit/util/purgeable_spec.rb +2 -8
- metadata +5 -2
@@ -8,7 +8,9 @@ module R10K
|
|
8
8
|
class Check < R10K::Action::Base
|
9
9
|
|
10
10
|
def call
|
11
|
-
pf = R10K::Puppetfile.new(@root,
|
11
|
+
pf = R10K::Puppetfile.new(@root,
|
12
|
+
{moduledir: @moduledir,
|
13
|
+
puppetfile_path: @puppetfile})
|
12
14
|
begin
|
13
15
|
pf.load!
|
14
16
|
$stderr.puts _("Syntax OK")
|
@@ -2,6 +2,7 @@ require 'r10k/puppetfile'
|
|
2
2
|
require 'r10k/errors/formatting'
|
3
3
|
require 'r10k/action/visitor'
|
4
4
|
require 'r10k/action/base'
|
5
|
+
require 'r10k/util/cleaner'
|
5
6
|
|
6
7
|
module R10K
|
7
8
|
module Action
|
@@ -9,33 +10,29 @@ module R10K
|
|
9
10
|
class Install < R10K::Action::Base
|
10
11
|
|
11
12
|
def call
|
12
|
-
@
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def visit_module(mod)
|
29
|
-
@force ||= false
|
30
|
-
logger.info _("Updating module %{mod_path}") % {mod_path: mod.path}
|
31
|
-
|
32
|
-
if mod.respond_to?(:desired_ref) && mod.desired_ref == :control_branch
|
33
|
-
logger.warn _("Cannot track control repo branch for content '%{name}' when not part of a 'deploy' action, will use default if available." % {name: mod.name})
|
13
|
+
@ok = true
|
14
|
+
begin
|
15
|
+
pf = R10K::Puppetfile.new(@root,
|
16
|
+
{moduledir: @moduledir,
|
17
|
+
puppetfile_path: @puppetfile,
|
18
|
+
force: @force || false})
|
19
|
+
pf.load!
|
20
|
+
pf.sync
|
21
|
+
|
22
|
+
R10K::Util::Cleaner.new(pf.managed_directories,
|
23
|
+
pf.desired_contents,
|
24
|
+
pf.purge_exclusions).purge!
|
25
|
+
|
26
|
+
rescue => e
|
27
|
+
@ok = false
|
28
|
+
logger.error R10K::Errors::Formatting.format_exception(e, @trace)
|
34
29
|
end
|
35
30
|
|
36
|
-
|
31
|
+
@ok
|
37
32
|
end
|
38
33
|
|
34
|
+
private
|
35
|
+
|
39
36
|
def allowed_initialize_opts
|
40
37
|
super.merge(root: :self, puppetfile: :self, moduledir: :self, force: :self )
|
41
38
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'r10k/puppetfile'
|
2
|
+
require 'r10k/util/cleaner'
|
2
3
|
require 'r10k/action/base'
|
3
4
|
require 'r10k/errors/formatting'
|
4
5
|
|
@@ -8,9 +9,14 @@ module R10K
|
|
8
9
|
class Purge < R10K::Action::Base
|
9
10
|
|
10
11
|
def call
|
11
|
-
pf = R10K::Puppetfile.new(@root,
|
12
|
+
pf = R10K::Puppetfile.new(@root,
|
13
|
+
{moduledir: @moduledir,
|
14
|
+
puppetfile_path: @puppetfile})
|
12
15
|
pf.load!
|
13
|
-
pf.
|
16
|
+
R10K::Util::Cleaner.new(pf.managed_directories,
|
17
|
+
pf.desired_contents,
|
18
|
+
pf.purge_exclusions).purge!
|
19
|
+
|
14
20
|
true
|
15
21
|
rescue => e
|
16
22
|
logger.error R10K::Errors::Formatting.format_exception(e, @trace)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module R10K
|
2
|
+
module ContentSynchronizer
|
3
|
+
|
4
|
+
def self.serial_accept(modules, visitor, loader)
|
5
|
+
visitor.visit(:puppetfile, loader) do
|
6
|
+
serial_sync(modules)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.serial_sync(modules)
|
11
|
+
modules.each do |mod|
|
12
|
+
mod.sync
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.concurrent_accept(modules, visitor, loader, pool_size, logger)
|
17
|
+
mods_queue = modules_visit_queue(modules, visitor, loader)
|
18
|
+
sync_queue(mods_queue, pool_size, logger)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.concurrent_sync(modules, pool_size, logger)
|
22
|
+
mods_queue = modules_sync_queue(modules)
|
23
|
+
sync_queue(mods_queue, pool_size, logger)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.sync_queue(mods_queue, pool_size, logger)
|
27
|
+
logger.debug _("Updating modules with %{pool_size} threads") % {pool_size: pool_size}
|
28
|
+
thread_pool = pool_size.times.map { sync_thread(mods_queue, logger) }
|
29
|
+
thread_exception = nil
|
30
|
+
|
31
|
+
# If any threads raise an exception the deployment is considered a failure.
|
32
|
+
# In that event clear the queue, wait for other threads to finish their
|
33
|
+
# current work, then re-raise the first exception caught.
|
34
|
+
begin
|
35
|
+
thread_pool.each(&:join)
|
36
|
+
rescue => e
|
37
|
+
logger.error _("Error during concurrent deploy of a module: %{message}") % {message: e.message}
|
38
|
+
mods_queue.clear
|
39
|
+
thread_exception ||= e
|
40
|
+
retry
|
41
|
+
ensure
|
42
|
+
raise thread_exception unless thread_exception.nil?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.modules_visit_queue(modules, visitor, loader)
|
47
|
+
Queue.new.tap do |queue|
|
48
|
+
visitor.visit(:puppetfile, loader) do
|
49
|
+
enqueue_modules(queue, modules)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.modules_sync_queue(modules)
|
55
|
+
Queue.new.tap do |queue|
|
56
|
+
enqueue_modules(queue, modules)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.enqueue_modules(queue, modules)
|
61
|
+
modules_by_cachedir = modules.group_by { |mod| mod.cachedir }
|
62
|
+
modules_without_vcs_cachedir = modules_by_cachedir.delete(:none) || []
|
63
|
+
|
64
|
+
modules_without_vcs_cachedir.each {|mod| queue << Array(mod) }
|
65
|
+
modules_by_cachedir.values.each {|mods| queue << mods }
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.sync_thread(mods_queue, logger)
|
69
|
+
Thread.new do
|
70
|
+
begin
|
71
|
+
while mods = mods_queue.pop(true) do
|
72
|
+
mods.each { |mod| mod.sync }
|
73
|
+
end
|
74
|
+
rescue ThreadError => e
|
75
|
+
logger.debug _("Module thread %{id} exiting: %{message}") % {message: e.message, id: Thread.current.object_id}
|
76
|
+
Thread.exit
|
77
|
+
rescue => e
|
78
|
+
Thread.main.raise(e)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/r10k/deployment.rb
CHANGED
@@ -118,7 +118,7 @@ module R10K
|
|
118
118
|
raise R10K::Error, _("Unable to load sources; the supplied configuration does not define the 'sources' key")
|
119
119
|
end
|
120
120
|
@_sources = sources.map do |(name, hash)|
|
121
|
-
R10K::Source.from_hash(name, hash)
|
121
|
+
R10K::Source.from_hash(name, hash.merge({overrides: @config[:overrides]}))
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'r10k/util/subprocess'
|
2
|
+
require 'r10k/logging'
|
2
3
|
|
3
4
|
# This class defines a common interface for environment implementations.
|
4
5
|
#
|
5
6
|
# @since 1.3.0
|
6
7
|
class R10K::Environment::Base
|
7
8
|
|
9
|
+
include R10K::Logging
|
10
|
+
|
8
11
|
# @!attribute [r] name
|
9
12
|
# @return [String] A name for this environment that is unique to the given source
|
10
13
|
attr_reader :name
|
@@ -44,11 +47,15 @@ class R10K::Environment::Base
|
|
44
47
|
@dirname = dirname
|
45
48
|
@options = options
|
46
49
|
@puppetfile_name = options.delete(:puppetfile_name)
|
50
|
+
@overrides = options.delete(:overrides) || {}
|
47
51
|
|
48
52
|
@full_path = File.join(@basedir, @dirname)
|
49
53
|
@path = Pathname.new(File.join(@basedir, @dirname))
|
50
54
|
|
51
|
-
@puppetfile = R10K::Puppetfile.new(@full_path,
|
55
|
+
@puppetfile = R10K::Puppetfile.new(@full_path,
|
56
|
+
{overrides: @overrides,
|
57
|
+
force: @overrides.dig(:modules, :force),
|
58
|
+
puppetfile_name: @puppetfile_name})
|
52
59
|
@puppetfile.environment = self
|
53
60
|
end
|
54
61
|
|
@@ -116,6 +123,19 @@ class R10K::Environment::Base
|
|
116
123
|
end
|
117
124
|
end
|
118
125
|
|
126
|
+
def deploy
|
127
|
+
puppetfile.load(@overrides.dig(:environments, :default_branch_override))
|
128
|
+
|
129
|
+
puppetfile.sync
|
130
|
+
|
131
|
+
if (@overrides.dig(:purging, :purge_levels) || []).include?(:puppetfile)
|
132
|
+
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!
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
119
139
|
def whitelist(user_whitelist=[])
|
120
140
|
user_whitelist.collect { |pattern| File.join(@full_path, pattern) }
|
121
141
|
end
|
data/lib/r10k/environment/git.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'r10k/logging'
|
2
1
|
require 'r10k/puppetfile'
|
3
2
|
require 'r10k/git/stateful_repository'
|
4
3
|
require 'forwardable'
|
@@ -8,8 +7,6 @@ require 'forwardable'
|
|
8
7
|
# @since 1.3.0
|
9
8
|
class R10K::Environment::Git < R10K::Environment::WithModules
|
10
9
|
|
11
|
-
include R10K::Logging
|
12
|
-
|
13
10
|
R10K::Environment.register(:git, self)
|
14
11
|
# Register git as the default environment type
|
15
12
|
R10K::Environment.register(nil, self)
|
data/lib/r10k/environment/svn.rb
CHANGED
@@ -7,8 +7,6 @@ require 'r10k/util/setopts'
|
|
7
7
|
# @since 1.3.0
|
8
8
|
class R10K::Environment::SVN < R10K::Environment::Base
|
9
9
|
|
10
|
-
include R10K::Logging
|
11
|
-
|
12
10
|
R10K::Environment.register(:svn, self)
|
13
11
|
|
14
12
|
# @!attribute [r] remote
|
@@ -46,12 +44,12 @@ class R10K::Environment::SVN < R10K::Environment::Base
|
|
46
44
|
super
|
47
45
|
setopts(options, {
|
48
46
|
# Standard option interface
|
49
|
-
:source
|
50
|
-
:version
|
51
|
-
:type
|
47
|
+
:source => :remote,
|
48
|
+
:version => :expected_revision,
|
49
|
+
:type => ::R10K::Util::Setopts::Ignore,
|
52
50
|
|
53
51
|
# Type-specific options
|
54
|
-
:remote
|
52
|
+
:remote => :self,
|
55
53
|
:username => :self,
|
56
54
|
:password => :self,
|
57
55
|
})
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'r10k/logging'
|
2
1
|
require 'r10k/util/purgeable'
|
3
2
|
|
4
3
|
# This abstract base class implements an environment that can include module
|
@@ -7,8 +6,6 @@ require 'r10k/util/purgeable'
|
|
7
6
|
# @since 3.4.0
|
8
7
|
class R10K::Environment::WithModules < R10K::Environment::Base
|
9
8
|
|
10
|
-
include R10K::Logging
|
11
|
-
|
12
9
|
# @!attribute [r] moduledir
|
13
10
|
# @return [String] The directory to install environment-defined modules
|
14
11
|
# into (default: #{basedir}/modules)
|
@@ -78,28 +75,39 @@ class R10K::Environment::WithModules < R10K::Environment::Base
|
|
78
75
|
def accept(visitor)
|
79
76
|
visitor.visit(:environment, self) do
|
80
77
|
@modules.each do |mod|
|
81
|
-
mod.
|
78
|
+
mod.sync
|
82
79
|
end
|
83
80
|
|
84
81
|
puppetfile.accept(visitor)
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
85
|
+
def deploy
|
86
|
+
@modules.each do |mod|
|
87
|
+
mod.sync
|
88
|
+
end
|
89
|
+
|
90
|
+
super
|
91
|
+
end
|
92
|
+
|
88
93
|
def load_modules(module_hash)
|
89
94
|
module_hash.each do |name, args|
|
95
|
+
if !args.is_a?(Hash)
|
96
|
+
args = { version: args }
|
97
|
+
end
|
98
|
+
|
90
99
|
add_module(name, args)
|
91
100
|
end
|
92
101
|
end
|
93
102
|
|
94
103
|
# @param [String] name
|
95
|
-
# @param [
|
104
|
+
# @param [Hash] args
|
96
105
|
def add_module(name, args)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
106
|
+
# symbolize keys in the args hash
|
107
|
+
args = args.inject({}) { |memo,(k,v)| memo[k.to_sym] = v; memo }
|
108
|
+
args[:overrides] = @overrides
|
101
109
|
|
102
|
-
if
|
110
|
+
if install_path = args.delete(:install_path)
|
103
111
|
install_path = resolve_install_path(install_path)
|
104
112
|
validate_install_path(install_path, name)
|
105
113
|
else
|
data/lib/r10k/module.rb
CHANGED
@@ -17,7 +17,7 @@ module R10K::Module
|
|
17
17
|
#
|
18
18
|
# @param [String] name The unique name of the module
|
19
19
|
# @param [String] basedir The root to install the module in
|
20
|
-
# @param [
|
20
|
+
# @param [Hash] args An arbitary Hash that specifies the implementation
|
21
21
|
# @param [R10K::Environment] environment Optional environment that this module is a part of
|
22
22
|
#
|
23
23
|
# @return [Object < R10K::Module] A member of the implementing subclass
|
data/lib/r10k/module/base.rb
CHANGED
@@ -51,7 +51,11 @@ class R10K::Module::Base
|
|
51
51
|
@owner, @name = parse_title(@title)
|
52
52
|
@path = Pathname.new(File.join(@dirname, @name))
|
53
53
|
@environment = environment
|
54
|
+
@overrides = args.delete(:overrides) || {}
|
54
55
|
@origin = 'external' # Expect Puppetfile or R10k::Environment to set this to a specific value
|
56
|
+
|
57
|
+
@requested_modules = @overrides.dig(:modules, :requested_modules) || []
|
58
|
+
@should_sync = (@requested_modules.empty? || @requested_modules.include?(@name))
|
55
59
|
end
|
56
60
|
|
57
61
|
# @deprecated
|
@@ -61,11 +65,22 @@ class R10K::Module::Base
|
|
61
65
|
end
|
62
66
|
|
63
67
|
# Synchronize this module with the indicated state.
|
64
|
-
# @
|
68
|
+
# @param [Hash] opts Deprecated
|
65
69
|
def sync(opts={})
|
66
70
|
raise NotImplementedError
|
67
71
|
end
|
68
72
|
|
73
|
+
def should_sync?
|
74
|
+
if @should_sync
|
75
|
+
logger.info _("Deploying module to %{path}") % {path: path}
|
76
|
+
true
|
77
|
+
else
|
78
|
+
logger.debug1(_("Only updating modules %{modules}, skipping module %{name}") % {modules: @requested_modules.inspect, name: name})
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
69
84
|
# Return the desired version of this module
|
70
85
|
# @abstract
|
71
86
|
def version
|
@@ -87,6 +102,7 @@ class R10K::Module::Base
|
|
87
102
|
raise NotImplementedError
|
88
103
|
end
|
89
104
|
|
105
|
+
# Deprecated
|
90
106
|
def accept(visitor)
|
91
107
|
visitor.visit(:module, self)
|
92
108
|
end
|
data/lib/r10k/module/forge.rb
CHANGED
@@ -13,7 +13,12 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
13
13
|
R10K::Module.register(self)
|
14
14
|
|
15
15
|
def self.implement?(name, args)
|
16
|
-
|
16
|
+
args[:type].to_s == 'forge' ||
|
17
|
+
(!!
|
18
|
+
((args.keys & %i{git svn type}).empty? &&
|
19
|
+
args.has_key?(:version) &&
|
20
|
+
name.match(%r[\w+[/-]\w+]) &&
|
21
|
+
valid_version?(args[:version])))
|
17
22
|
end
|
18
23
|
|
19
24
|
def self.valid_version?(expected_version)
|
@@ -40,28 +45,29 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
40
45
|
@metadata_file = R10K::Module::MetadataFile.new(path + 'metadata.json')
|
41
46
|
@metadata = @metadata_file.read
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
@expected_version = opts || current_version || :latest
|
52
|
-
end
|
48
|
+
setopts(opts, {
|
49
|
+
# Standard option interface
|
50
|
+
:version => :expected_version,
|
51
|
+
:source => ::R10K::Util::Setopts::Ignore,
|
52
|
+
:type => ::R10K::Util::Setopts::Ignore,
|
53
|
+
})
|
54
|
+
|
55
|
+
@expected_version ||= current_version || :latest
|
53
56
|
|
54
57
|
@v3_module = PuppetForge::V3::Module.new(:slug => @title)
|
55
58
|
end
|
56
59
|
|
60
|
+
# @param [Hash] opts Deprecated
|
57
61
|
def sync(opts={})
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
if should_sync?
|
63
|
+
case status
|
64
|
+
when :absent
|
65
|
+
install
|
66
|
+
when :outdated
|
67
|
+
upgrade
|
68
|
+
when :mismatched
|
69
|
+
reinstall
|
70
|
+
end
|
65
71
|
end
|
66
72
|
end
|
67
73
|
|