r10k 0.0.9 → 1.0.0rc1
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.
- 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
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'r10k/task'
|
2
|
+
require 'r10k/task/puppetfile'
|
3
|
+
|
4
|
+
module R10K
|
5
|
+
module Task
|
6
|
+
module Environment
|
7
|
+
class Deploy < R10K::Task::Base
|
8
|
+
|
9
|
+
attr_writer :update_puppetfile
|
10
|
+
|
11
|
+
def initialize(environment)
|
12
|
+
@environment = environment
|
13
|
+
|
14
|
+
@update_puppetfile = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
logger.notice "Deploying environment #{@environment.dirname}"
|
19
|
+
@environment.sync
|
20
|
+
|
21
|
+
if @update_puppetfile
|
22
|
+
task = R10K::Task::Puppetfile::Sync.new(@environment.puppetfile)
|
23
|
+
task_runner.insert_task_after(self, task)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'r10k/task'
|
2
|
+
|
3
|
+
module R10K
|
4
|
+
module Task
|
5
|
+
module Module
|
6
|
+
class Sync < R10K::Task::Base
|
7
|
+
def initialize(mod)
|
8
|
+
@mod = mod
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
logger.info "Deploying #{@mod.name} into #{@mod.basedir}"
|
13
|
+
@mod.sync
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'r10k/task'
|
2
|
+
require 'r10k/task/module'
|
3
|
+
|
4
|
+
require 'r10k/task_runner'
|
5
|
+
|
6
|
+
module R10K
|
7
|
+
module Task
|
8
|
+
module Puppetfile
|
9
|
+
class Sync < R10K::Task::Base
|
10
|
+
def initialize(puppetfile)
|
11
|
+
@puppetfile = puppetfile
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
logger.info "Loading modules from Puppetfile into queue"
|
16
|
+
|
17
|
+
@puppetfile.load
|
18
|
+
@puppetfile.modules.each do |mod|
|
19
|
+
task = R10K::Task::Module::Sync.new(mod)
|
20
|
+
task_runner.insert_task_after(self, task)
|
21
|
+
end
|
22
|
+
|
23
|
+
purge_task = Purge.new(@puppetfile)
|
24
|
+
task_runner.append_task purge_task
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class DeployModules < R10K::Task::Base
|
29
|
+
|
30
|
+
attr_accessor :module_names
|
31
|
+
|
32
|
+
def initialize(puppetfile)
|
33
|
+
@puppetfile = puppetfile
|
34
|
+
end
|
35
|
+
|
36
|
+
def call
|
37
|
+
logger.debug "Updating module list for Puppetfile #{@puppetfile.basedir}"
|
38
|
+
@puppetfile.load
|
39
|
+
load_modulemap!
|
40
|
+
|
41
|
+
existing = @modulemap.keys
|
42
|
+
|
43
|
+
warn_on_missing(existing, @module_names)
|
44
|
+
|
45
|
+
to_deploy = existing & @module_names
|
46
|
+
|
47
|
+
to_deploy.each do |mod_name|
|
48
|
+
mod = @modulemap[mod_name]
|
49
|
+
task = R10K::Task::Module::Sync.new(mod)
|
50
|
+
task_runner.insert_task_after(self, task)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def warn_on_missing(existing, requested)
|
57
|
+
missing_modules = requested - existing
|
58
|
+
|
59
|
+
unless missing_modules.empty?
|
60
|
+
task_runner.succeeded = false
|
61
|
+
|
62
|
+
missing_modules.each do |missing|
|
63
|
+
logger.warn "Unable to deploy module #{missing}: not listed in #{@puppetfile.puppetfile_path}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def load_modulemap!
|
69
|
+
@modulemap = @puppetfile.modules.inject({}) do |hash, mod|
|
70
|
+
hash[mod.name] = mod
|
71
|
+
hash
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Purge < R10K::Task::Base
|
77
|
+
def initialize(puppetfile)
|
78
|
+
@puppetfile = puppetfile
|
79
|
+
end
|
80
|
+
|
81
|
+
def call
|
82
|
+
moduledir = @puppetfile.moduledir
|
83
|
+
|
84
|
+
@puppetfile.load
|
85
|
+
|
86
|
+
stale_mods = @puppetfile.stale_contents
|
87
|
+
|
88
|
+
if stale_mods.empty?
|
89
|
+
logger.debug "No stale modules in #{moduledir}"
|
90
|
+
else
|
91
|
+
logger.info "Purging stale modules from #{moduledir}"
|
92
|
+
logger.debug "Stale modules in #{moduledir}: #{stale_mods.join(', ')}"
|
93
|
+
@puppetfile.purge!
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'r10k/task'
|
2
|
+
require 'r10k/logging'
|
3
|
+
|
4
|
+
module R10K
|
5
|
+
class TaskRunner
|
6
|
+
|
7
|
+
include R10K::Logging
|
8
|
+
|
9
|
+
attr_writer :succeeded
|
10
|
+
|
11
|
+
def initialize(opts)
|
12
|
+
@tasks = []
|
13
|
+
@succeeded = true
|
14
|
+
@errors = {}
|
15
|
+
|
16
|
+
@trace = opts.delete(:trace)
|
17
|
+
|
18
|
+
raise "Unrecognized options: #{opts.keys.join(', ')}" unless opts.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
catch :abort do
|
23
|
+
until @tasks.empty?
|
24
|
+
current = @tasks.first
|
25
|
+
current.task_runner = self
|
26
|
+
begin
|
27
|
+
current.call
|
28
|
+
rescue Interrupt => e
|
29
|
+
logger.error "Aborted!"
|
30
|
+
$stderr.puts e.backtrace.join("\n").red if @trace
|
31
|
+
@succeeded = false
|
32
|
+
throw :abort
|
33
|
+
rescue => e
|
34
|
+
logger.error "Task #{current} failed while running: #{e.message}"
|
35
|
+
$stderr.puts e.backtrace.join("\n").red if @trace
|
36
|
+
|
37
|
+
@errors[current] = e
|
38
|
+
@succeeded = false
|
39
|
+
end
|
40
|
+
@tasks.shift
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def prepend_task(task)
|
46
|
+
@tasks.unshift task
|
47
|
+
end
|
48
|
+
|
49
|
+
def append_task(task)
|
50
|
+
@tasks << task
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param [R10K::Task] task_index The task to insert the task after
|
54
|
+
# @param [R10K::Task] new_task The task to insert
|
55
|
+
def insert_task_after(task_index, new_task)
|
56
|
+
if (index = @tasks.index(task_index))
|
57
|
+
index += 1
|
58
|
+
@tasks.insert(index, new_task)
|
59
|
+
else
|
60
|
+
@tasks.insert(0, new_task)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def succeeded?
|
65
|
+
@succeeded
|
66
|
+
end
|
67
|
+
|
68
|
+
def exit_value
|
69
|
+
@succeeded ? 0 : 1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
module R10K
|
3
|
+
module Util
|
4
|
+
module Purgeable
|
5
|
+
# Mixin for purging stale directory contents.
|
6
|
+
#
|
7
|
+
# @abstract Classes using this mixin need to implement {#managed_directory} and
|
8
|
+
# {#desired_contents}
|
9
|
+
|
10
|
+
# @!method desired_contents
|
11
|
+
# @abstract Including classes must implement this method to list the
|
12
|
+
# expected filenames of managed_directory
|
13
|
+
# @return [Array<String>] A list of directory contents that should be present
|
14
|
+
|
15
|
+
# @!method managed_directory
|
16
|
+
# @abstract Including classes must implement this method to return the
|
17
|
+
# path to the directory that can be purged
|
18
|
+
# @return [String] The path to the directory to be purged
|
19
|
+
|
20
|
+
# @return [Array<String>] The present directory entries in `self.managed_directory`
|
21
|
+
def current_contents
|
22
|
+
dir = self.managed_directory
|
23
|
+
glob_exp = File.join(dir, '*')
|
24
|
+
|
25
|
+
Dir.glob(glob_exp).map do |fname|
|
26
|
+
File.basename fname
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Array<String>] Directory contents that are expected but not present
|
31
|
+
def pending_contents
|
32
|
+
desired_contents - current_contents
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Array<String>] Directory contents that are present but not expected
|
36
|
+
def stale_contents
|
37
|
+
current_contents - desired_contents
|
38
|
+
end
|
39
|
+
|
40
|
+
# Forcibly remove all unmanaged content in `self.managed_directory`
|
41
|
+
def purge!
|
42
|
+
stale_contents.each do |fname|
|
43
|
+
fpath = File.join(self.managed_directory, fname)
|
44
|
+
FileUtils.rm_rf fpath, :secure => true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/r10k/version.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'r10k/deployment/environment'
|
3
|
+
|
4
|
+
describe R10K::Deployment::Environment do
|
5
|
+
let(:remote) { 'git://github.com/adrienthebo/r10k-fixture-repo' }
|
6
|
+
let(:ref) { 'master' }
|
7
|
+
|
8
|
+
describe 'dirname' do
|
9
|
+
it 'uses the ref as the default dirname' do
|
10
|
+
subject = described_class.new(ref, remote, '/tmp')
|
11
|
+
subject.dirname.should == 'master'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'allows a specific dirname to be set' do
|
15
|
+
subject = described_class.new(ref, remote, '/tmp', 'sourcename_master')
|
16
|
+
subject.dirname.should == 'sourcename_master'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe R10K::Git::Cache, 'memoizing objects' do
|
4
|
+
|
5
|
+
it "returns the same object when a remote is duplicated" do
|
6
|
+
first = described_class.new('foo')
|
7
|
+
second = described_class.new('foo')
|
8
|
+
|
9
|
+
first.should be second
|
10
|
+
end
|
11
|
+
|
12
|
+
it "wipes the memoized objects when .clear! is called" do
|
13
|
+
first = described_class.new('foo')
|
14
|
+
described_class.clear!
|
15
|
+
second = described_class.new('foo')
|
16
|
+
|
17
|
+
first.should_not be second
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe R10K::Git::Cache do
|
22
|
+
|
23
|
+
after do
|
24
|
+
described_class.clear!
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'setting the cache root' do
|
28
|
+
it 'defaults to ~/.r10k/git' do
|
29
|
+
described_class.new('foo').cache_root.should match %r[/\.r10k/git]
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'uses the class cache root if set' do
|
33
|
+
described_class.stubs(:cache_root).returns '/var/spool/r10k'
|
34
|
+
described_class.new('foo').cache_root.should == '/var/spool/r10k'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe R10K::Git::WorkingDir do
|
4
|
+
|
5
|
+
before do
|
6
|
+
R10K::Git::Cache.stubs(:cache_root).returns '/tmp'
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "initializing" do
|
10
|
+
it "generates a new cache for the remote" do
|
11
|
+
wd = described_class.new('master', 'git://github.com/adrienthebo/r10k-fixture-repo', '/tmp')
|
12
|
+
wd.cache.should be_kind_of R10K::Git::Cache
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'r10k/module/forge'
|
2
|
+
require 'semver'
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe R10K::Module::Forge do
|
6
|
+
before :each do
|
7
|
+
Object.expects(:systemu).never
|
8
|
+
end
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
s = stub(:debug2 => nil, :debug1 => nil, :debug => nil, :info => nil)
|
12
|
+
described_class.any_instance.stubs(:logger).returns s
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "implementing the Puppetfile spec" do
|
16
|
+
it "should implement 'branan/eight_hundred', '8.0.0'" do
|
17
|
+
described_class.should be_implement('branan/eight_hundred', '8.0.0')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should fail with an invalid full name" do
|
21
|
+
described_class.should_not be_implement('branan-eight_hundred', '8.0.0')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should fail with an invalid version" do
|
25
|
+
described_class.should_not be_implement('branan-eight_hundred', 'not a semantic version')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "setting attributes" do
|
30
|
+
subject { described_class.new('branan/eight_hundred', '/moduledir', '8.0.0') }
|
31
|
+
|
32
|
+
its(:name) { should eq 'eight_hundred' }
|
33
|
+
its(:owner) { should eq 'branan' }
|
34
|
+
its(:full_name) { should eq 'branan/eight_hundred' }
|
35
|
+
its(:basedir) { should eq '/moduledir' }
|
36
|
+
its(:full_path) { should eq '/moduledir/eight_hundred' }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "when syncing" do
|
40
|
+
let(:fixture_modulepath) { File.expand_path('spec/fixtures/module/forge', PROJECT_ROOT) }
|
41
|
+
let(:empty_modulepath) { File.expand_path('spec/fixtures/empty', PROJECT_ROOT) }
|
42
|
+
|
43
|
+
describe "and the module is in sync" do
|
44
|
+
subject { described_class.new('branan/eight_hundred', fixture_modulepath, '8.0.0') }
|
45
|
+
|
46
|
+
it { should be_insync }
|
47
|
+
its(:version) { should eq '8.0.0' }
|
48
|
+
its(:current_version) { should eq '8.0.0' }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "and the desired version is newer than the installed version" do
|
52
|
+
subject { described_class.new('branan/eight_hundred', fixture_modulepath, '80.0.0') }
|
53
|
+
|
54
|
+
it { should_not be_insync }
|
55
|
+
its(:version) { should eq '80.0.0' }
|
56
|
+
its(:current_version) { should eq '8.0.0' }
|
57
|
+
|
58
|
+
it "should try to upgrade the module" do
|
59
|
+
# "v80.0.0" ? Seriously? Where did the "v" come from?
|
60
|
+
expected = %w{upgrade --version=v80.0.0 --ignore-dependencies branan/eight_hundred}
|
61
|
+
subject.expects(:pmt).with(expected)
|
62
|
+
subject.sync!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "and the desired version is older than the installed version" do
|
67
|
+
subject { described_class.new('branan/eight_hundred', fixture_modulepath, '7.0.0') }
|
68
|
+
|
69
|
+
it { should_not be_insync }
|
70
|
+
its(:version) { should eq '7.0.0' }
|
71
|
+
its(:current_version) { should eq '8.0.0' }
|
72
|
+
|
73
|
+
it "should try to downgrade the module" do
|
74
|
+
# Again with the magical "v" prefix to the version.
|
75
|
+
expected = %w{upgrade --version=v7.0.0 --ignore-dependencies branan/eight_hundred}
|
76
|
+
subject.expects(:pmt).with(expected)
|
77
|
+
subject.sync!
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "and the module is not installed" do
|
82
|
+
subject { described_class.new('branan/eight_hundred', empty_modulepath, '8.0.0') }
|
83
|
+
|
84
|
+
it { should_not be_insync }
|
85
|
+
its(:version) { should eq '8.0.0' }
|
86
|
+
its(:current_version) { should eq SemVer::MIN }
|
87
|
+
|
88
|
+
it "should try to install the module" do
|
89
|
+
expected = %w{install --version=v8.0.0 --ignore-dependencies branan/eight_hundred}
|
90
|
+
subject.expects(:pmt).with(expected)
|
91
|
+
subject.sync!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|