r10k 0.0.9 → 1.0.0rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/r10k +1 -1
- data/lib/r10k.rb +0 -4
- data/lib/r10k/cli.rb +9 -5
- data/lib/r10k/cli/deploy.rb +108 -0
- data/lib/r10k/cli/environment.rb +5 -1
- data/lib/r10k/cli/environment/deploy.rb +6 -28
- data/lib/r10k/cli/environment/list.rb +6 -10
- data/lib/r10k/cli/environment/stale.rb +6 -16
- data/lib/r10k/cli/module.rb +5 -1
- data/lib/r10k/cli/module/deploy.rb +5 -32
- data/lib/r10k/cli/module/list.rb +6 -27
- data/lib/r10k/cli/puppetfile.rb +76 -0
- data/lib/r10k/cli/synchronize.rb +8 -24
- data/lib/r10k/cli/version.rb +22 -0
- data/lib/r10k/deployment.rb +55 -26
- data/lib/r10k/deployment/config.rb +69 -0
- data/lib/r10k/{config → deployment/config}/loader.rb +9 -5
- data/lib/r10k/deployment/environment.rb +88 -0
- data/lib/r10k/deployment/source.rb +79 -0
- data/lib/r10k/errors.rb +3 -5
- data/lib/r10k/execution.rb +43 -0
- data/lib/r10k/git/cache.rb +131 -0
- data/lib/r10k/git/errors.rb +34 -0
- data/lib/r10k/git/repository.rb +74 -0
- data/lib/r10k/git/working_dir.rb +142 -0
- data/lib/r10k/logging.rb +6 -2
- data/lib/r10k/module.rb +10 -13
- data/lib/r10k/module/forge.rb +35 -22
- data/lib/r10k/module/git.rb +18 -8
- data/lib/r10k/puppetfile.rb +107 -0
- data/lib/r10k/task.rb +13 -0
- data/lib/r10k/task/deployment.rb +151 -0
- data/lib/r10k/task/environment.rb +29 -0
- data/lib/r10k/task/module.rb +18 -0
- data/lib/r10k/task/puppetfile.rb +99 -0
- data/lib/r10k/task_runner.rb +72 -0
- data/lib/r10k/util/purgeable.rb +50 -0
- data/lib/r10k/version.rb +1 -1
- data/spec/unit/deployment/environment_spec.rb +19 -0
- data/spec/unit/git/cache_spec.rb +37 -0
- data/spec/unit/git/working_dir_spec.rb +15 -0
- data/spec/unit/module/forge_spec.rb +95 -0
- data/spec/unit/module_spec.rb +29 -0
- metadata +79 -44
- data/lib/r10k/action.rb +0 -7
- data/lib/r10k/action/environment.rb +0 -74
- data/lib/r10k/action/module.rb +0 -36
- data/lib/r10k/cli/cache.rb +0 -32
- data/lib/r10k/config.rb +0 -46
- data/lib/r10k/deployment/environment_collection.rb +0 -75
- data/lib/r10k/librarian.rb +0 -31
- data/lib/r10k/librarian/dsl.rb +0 -20
- data/lib/r10k/root.rb +0 -98
- data/lib/r10k/synchro/git.rb +0 -226
data/lib/r10k/module.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'r10k'
|
2
2
|
|
3
|
-
|
3
|
+
module R10K::Module
|
4
4
|
|
5
5
|
# Register an inheriting class for later generation
|
6
|
-
def self.
|
6
|
+
def self.included(klass)
|
7
|
+
klass.extend self
|
7
8
|
@klasses ||= []
|
8
9
|
@klasses << klass
|
9
10
|
end
|
@@ -16,28 +17,24 @@ class R10K::Module
|
|
16
17
|
# with `name, args`, and generates an object of that class.
|
17
18
|
#
|
18
19
|
# @param [String] name The unique name of the module
|
19
|
-
# @param [String]
|
20
|
+
# @param [String] basedir The root to install the module in
|
20
21
|
# @param [Object] args An arbitary value or set of values that specifies the implementation
|
21
22
|
#
|
22
23
|
# @return [Object < R10K::Module] A member of the implementing subclass
|
23
|
-
def self.new(name,
|
24
|
-
if implementation = @klasses.find { |klass| klass.
|
25
|
-
obj = implementation.
|
26
|
-
obj.send(:initialize, name, path, args)
|
24
|
+
def self.new(name, basedir, args)
|
25
|
+
if implementation = @klasses.find { |klass| klass.implement?(name, args) }
|
26
|
+
obj = implementation.new(name, basedir, args)
|
27
27
|
obj
|
28
28
|
else
|
29
29
|
raise "Module #{name} with args #{args.inspect} doesn't have an implementation. (Are you using the right arguments?)"
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
attr_accessor :name, :
|
34
|
-
|
35
|
-
def initialize(name, path, args)
|
36
|
-
@name, @path, @args = name, path, args
|
37
|
-
end
|
33
|
+
attr_accessor :name, :basedir
|
38
34
|
|
35
|
+
# @return [String] The full filesystem path to the module.
|
39
36
|
def full_path
|
40
|
-
File.join(@
|
37
|
+
File.join(@basedir, @name)
|
41
38
|
end
|
42
39
|
end
|
43
40
|
|
data/lib/r10k/module/forge.rb
CHANGED
@@ -7,70 +7,81 @@ require 'systemu'
|
|
7
7
|
require 'semver'
|
8
8
|
require 'json'
|
9
9
|
|
10
|
-
|
10
|
+
module R10K
|
11
|
+
module Module
|
12
|
+
class Forge
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
include R10K::Module
|
15
|
+
|
16
|
+
def self.implement?(name, args)
|
17
|
+
!!(name.match %r[\w+/\w+])
|
14
18
|
end
|
15
19
|
|
16
20
|
include R10K::Logging
|
17
21
|
|
18
|
-
|
19
|
-
super
|
22
|
+
attr_accessor :version, :owner, :full_name
|
20
23
|
|
24
|
+
def initialize(name, basedir, args)
|
21
25
|
@full_name = name
|
26
|
+
@basedir = basedir
|
22
27
|
|
23
28
|
@owner, @name = name.split('/')
|
24
|
-
|
29
|
+
|
30
|
+
if args.is_a? String
|
31
|
+
@version = SemVer.new(args)
|
32
|
+
end
|
25
33
|
end
|
26
34
|
|
27
|
-
def sync
|
35
|
+
def sync(options = {})
|
28
36
|
return if insync?
|
29
37
|
|
30
38
|
if insync?
|
31
|
-
logger.debug1 "Module #{@full_name} already matches version #{@version}"
|
39
|
+
#logger.debug1 "Module #{@full_name} already matches version #{@version}"
|
32
40
|
elsif File.exist? metadata_path
|
33
|
-
logger.debug "Module #{@full_name} is installed but doesn't match version #{@version}, upgrading"
|
41
|
+
#logger.debug "Module #{@full_name} is installed but doesn't match version #{@version}, upgrading"
|
34
42
|
cmd = []
|
35
43
|
cmd << 'upgrade'
|
36
|
-
cmd << "--version=#{@version}"
|
44
|
+
cmd << "--version=#{@version}" if @version
|
37
45
|
cmd << "--ignore-dependencies"
|
38
46
|
cmd << @full_name
|
39
47
|
pmt cmd
|
40
48
|
else
|
41
|
-
logger.debug "Module #{@full_name} is not installed"
|
49
|
+
#logger.debug "Module #{@full_name} is not installed"
|
42
50
|
cmd = []
|
43
51
|
cmd << 'install'
|
44
|
-
cmd << "--version=#{@version}"
|
52
|
+
cmd << "--version=#{@version}" if @version
|
45
53
|
cmd << "--ignore-dependencies"
|
46
54
|
cmd << @full_name
|
47
55
|
pmt cmd
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
59
|
+
# @return [SemVer, NilClass]
|
60
|
+
def version
|
61
|
+
if metadata
|
62
|
+
SemVer.new(metadata['version'])
|
63
|
+
else
|
64
|
+
SemVer::MIN
|
65
|
+
end
|
55
66
|
end
|
56
67
|
|
57
68
|
def insync?
|
58
|
-
@version ==
|
59
|
-
rescue
|
60
|
-
false
|
69
|
+
@version == version
|
61
70
|
end
|
62
71
|
|
63
72
|
def metadata
|
64
|
-
JSON.parse(File.read(metadata_path))
|
73
|
+
@metadata = JSON.parse(File.read(metadata_path)) rescue nil
|
65
74
|
end
|
66
75
|
|
67
76
|
def metadata_path
|
68
77
|
File.join(full_path, 'metadata.json')
|
69
78
|
end
|
70
79
|
|
80
|
+
private
|
81
|
+
|
71
82
|
def pmt(args)
|
72
|
-
cmd = "puppet module --modulepath '#{@
|
73
|
-
log_event = "puppet module #{args.join(' ')}, modulepath: #{@
|
83
|
+
cmd = "puppet module --modulepath '#{@basedir}' #{args.join(' ')}"
|
84
|
+
log_event = "puppet module #{args.join(' ')}, modulepath: #{@basedir.inspect}"
|
74
85
|
logger.debug1 "Execute: #{cmd}"
|
75
86
|
|
76
87
|
status, stdout, stderr = systemu(cmd)
|
@@ -88,3 +99,5 @@ class R10K::Module::Forge < R10K::Module
|
|
88
99
|
stdout
|
89
100
|
end
|
90
101
|
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/r10k/module/git.rb
CHANGED
@@ -1,24 +1,34 @@
|
|
1
1
|
require 'r10k'
|
2
2
|
require 'r10k/module'
|
3
|
-
require 'r10k/
|
3
|
+
require 'r10k/git/working_dir'
|
4
|
+
require 'forwardable'
|
4
5
|
|
5
|
-
|
6
|
+
module R10K
|
7
|
+
module Module
|
8
|
+
class Git
|
9
|
+
include R10K::Module
|
6
10
|
|
7
|
-
def self.
|
11
|
+
def self.implement?(name, args)
|
8
12
|
args.is_a? Hash and args.has_key?(:git)
|
9
13
|
rescue
|
10
14
|
false
|
11
15
|
end
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
extend Forwardable
|
18
|
+
def_delegator :@working_dir, :sync
|
19
|
+
|
20
|
+
def initialize(name, basedir, args)
|
21
|
+
@name, @basedir, @args = name, basedir, args
|
15
22
|
|
16
23
|
@remote = @args[:git]
|
17
24
|
@ref = (@args[:ref] || 'master')
|
25
|
+
|
26
|
+
@working_dir = R10K::Git::WorkingDir.new(@ref, @remote, @basedir, @name)
|
18
27
|
end
|
19
28
|
|
20
|
-
def
|
21
|
-
|
22
|
-
synchro.sync(full_path, @ref, options)
|
29
|
+
def version
|
30
|
+
@ref
|
23
31
|
end
|
24
32
|
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'r10k/module'
|
2
|
+
require 'r10k/logging'
|
3
|
+
require 'r10k/util/purgeable'
|
4
|
+
|
5
|
+
module R10K
|
6
|
+
class Puppetfile
|
7
|
+
# Defines the data members of a Puppetfile
|
8
|
+
|
9
|
+
include R10K::Logging
|
10
|
+
|
11
|
+
# @!attribute [r] forge
|
12
|
+
# @return [String] The URL to use for the Puppet Forge
|
13
|
+
attr_reader :forge
|
14
|
+
|
15
|
+
# @!attribute [r] modules
|
16
|
+
# @return [Array<R10K::Module>]
|
17
|
+
attr_reader :modules
|
18
|
+
|
19
|
+
# @!attribute [r] basedir
|
20
|
+
# @return [String] The base directory that contains the Puppetfile
|
21
|
+
attr_reader :basedir
|
22
|
+
|
23
|
+
# @!attribute [r] moduledir
|
24
|
+
# @return [String] The directory to install the modules #{basedir}/modules
|
25
|
+
attr_reader :moduledir
|
26
|
+
|
27
|
+
# @!attrbute [r] puppetfile_path
|
28
|
+
# @return [String] The path to the Puppetfile
|
29
|
+
attr_reader :puppetfile_path
|
30
|
+
|
31
|
+
# @param [String] basedir
|
32
|
+
# @param [String] puppetfile The path to the Puppetfile, default to #{basedir}/Puppetfile
|
33
|
+
def initialize(basedir, moduledir = nil, puppetfile = nil)
|
34
|
+
@basedir = basedir
|
35
|
+
@moduledir = moduledir || File.join(basedir, 'modules')
|
36
|
+
@puppetfile_path = puppetfile || File.join(basedir, 'Puppetfile')
|
37
|
+
|
38
|
+
@modules = []
|
39
|
+
@forge = 'forge.puppetlabs.com'
|
40
|
+
end
|
41
|
+
|
42
|
+
def load
|
43
|
+
if File.readable? @puppetfile_path
|
44
|
+
self.load!
|
45
|
+
else
|
46
|
+
logger.debug "Puppetfile #{@puppetfile_path.inspect} missing or unreadable"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def load!
|
51
|
+
dsl = R10K::Puppetfile::DSL.new(self)
|
52
|
+
dsl.instance_eval(puppetfile_contents, @puppetfile_path)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param [String] forge
|
56
|
+
def set_forge(forge)
|
57
|
+
@forge = forge
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param [String] name
|
61
|
+
# @param [*Object] args
|
62
|
+
def add_module(name, args)
|
63
|
+
@modules << R10K::Module.new(name, @moduledir, args)
|
64
|
+
end
|
65
|
+
|
66
|
+
include R10K::Util::Purgeable
|
67
|
+
|
68
|
+
def managed_directory
|
69
|
+
@moduledir
|
70
|
+
end
|
71
|
+
|
72
|
+
# List all modules that should exist in the module directory
|
73
|
+
# @note This implements a required method for the Purgeable mixin
|
74
|
+
# @return [Array<String>]
|
75
|
+
def desired_contents
|
76
|
+
@modules.map { |mod| mod.name }
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def puppetfile_contents
|
82
|
+
File.read(@puppetfile_path)
|
83
|
+
end
|
84
|
+
|
85
|
+
class DSL
|
86
|
+
# A barebones implementation of the Puppetfile DSL
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
|
90
|
+
def initialize(librarian)
|
91
|
+
@librarian = librarian
|
92
|
+
end
|
93
|
+
|
94
|
+
def mod(name, args = [])
|
95
|
+
@librarian.add_module(name, args)
|
96
|
+
end
|
97
|
+
|
98
|
+
def forge(location)
|
99
|
+
@librarian.set_forge(location)
|
100
|
+
end
|
101
|
+
|
102
|
+
def method_missing(method, *args)
|
103
|
+
raise NoMethodError, "unrecognized declaration '#{method}'"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/r10k/task.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'r10k/task'
|
2
|
+
require 'r10k/task_runner'
|
3
|
+
|
4
|
+
require 'r10k/task/environment'
|
5
|
+
require 'r10k/task/puppetfile'
|
6
|
+
|
7
|
+
module R10K
|
8
|
+
module Task
|
9
|
+
module Deployment
|
10
|
+
module SharedBehaviors
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def load_environments!
|
15
|
+
@environments = @deployment.environments.inject({}) do |hash, env|
|
16
|
+
hash[env.dirname] = env
|
17
|
+
hash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param [Array<String>] names The list of environments to deploy.
|
22
|
+
#
|
23
|
+
def with_environments(names = [], &block)
|
24
|
+
load_environments!
|
25
|
+
|
26
|
+
# If an explicit list of environments were not given, deploy everything
|
27
|
+
if names.size > 0
|
28
|
+
to_deploy = names
|
29
|
+
else
|
30
|
+
to_deploy = @environments.keys
|
31
|
+
end
|
32
|
+
|
33
|
+
to_deploy.each do |env_name|
|
34
|
+
if (env = @environments[env_name])
|
35
|
+
yield env
|
36
|
+
else
|
37
|
+
logger.warn "Environment #{env_name} not found in any source"
|
38
|
+
task_runner.succeeded = false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class DeployEnvironments < R10K::Task::Base
|
45
|
+
|
46
|
+
include SharedBehaviors
|
47
|
+
|
48
|
+
# @!attribute environment_names
|
49
|
+
# @return [Array<String>] A list of environments to deploy, by name.
|
50
|
+
attr_accessor :environment_names
|
51
|
+
|
52
|
+
# @!attribute update_puppetfile
|
53
|
+
# @return [TrueClass, FalseClass] Whether to deploy modules in a puppetfile
|
54
|
+
attr_accessor :update_puppetfile
|
55
|
+
|
56
|
+
def initialize(deployment)
|
57
|
+
@deployment = deployment
|
58
|
+
@update_puppetfile = false
|
59
|
+
@environment_names = []
|
60
|
+
end
|
61
|
+
|
62
|
+
def call
|
63
|
+
logger.info "Loading environments from all sources"
|
64
|
+
@deployment.fetch_sources
|
65
|
+
|
66
|
+
with_environments(@environment_names) do |env|
|
67
|
+
task = R10K::Task::Environment::Deploy.new(env)
|
68
|
+
task.update_puppetfile = @update_puppetfile
|
69
|
+
task_runner.insert_task_after(self, task)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class DeployModules < R10K::Task::Base
|
75
|
+
|
76
|
+
include SharedBehaviors
|
77
|
+
|
78
|
+
attr_accessor :module_names
|
79
|
+
|
80
|
+
# @!attribute environment_names
|
81
|
+
# @return [Array<String>] A list of environments to update modules
|
82
|
+
attr_accessor :environment_names
|
83
|
+
|
84
|
+
def initialize(deployment)
|
85
|
+
@deployment = deployment
|
86
|
+
@environment_names = []
|
87
|
+
end
|
88
|
+
|
89
|
+
def call
|
90
|
+
with_environments(@environment_names) do |env|
|
91
|
+
puppetfile = env.puppetfile
|
92
|
+
|
93
|
+
task = R10K::Task::Puppetfile::DeployModules.new(puppetfile)
|
94
|
+
task.module_names = module_names
|
95
|
+
|
96
|
+
task_runner.insert_task_after(self, task)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class PurgeEnvironments < R10K::Task::Base
|
102
|
+
|
103
|
+
def initialize(deployment)
|
104
|
+
@deployment = deployment
|
105
|
+
end
|
106
|
+
|
107
|
+
def call
|
108
|
+
@deployment.sources.each do |source|
|
109
|
+
stale_envs = source.stale_contents
|
110
|
+
|
111
|
+
dir = source.managed_directory
|
112
|
+
|
113
|
+
if stale_envs.empty?
|
114
|
+
logger.debug "No stale environments in #{dir}"
|
115
|
+
else
|
116
|
+
logger.info "Purging stale environments from #{dir}"
|
117
|
+
logger.debug "Stale modules in #{dir}: #{stale_envs.join(', ')}"
|
118
|
+
source.purge!
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Display < R10K::Task::Base
|
125
|
+
|
126
|
+
attr_accessor :puppetfile
|
127
|
+
|
128
|
+
def initialize(deployment)
|
129
|
+
@deployment = deployment
|
130
|
+
end
|
131
|
+
|
132
|
+
def call
|
133
|
+
@deployment.environments.each do |env|
|
134
|
+
|
135
|
+
puts " - #{env.dirname}"
|
136
|
+
|
137
|
+
if @puppetfile
|
138
|
+
puppetfile = env.puppetfile
|
139
|
+
puppetfile.load
|
140
|
+
|
141
|
+
puppetfile.modules.each do |mod|
|
142
|
+
puts " - #{mod.name} (#{mod.version})"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|