r10k 0.0.9 → 1.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/bin/r10k +1 -1
  2. data/lib/r10k.rb +0 -4
  3. data/lib/r10k/cli.rb +9 -5
  4. data/lib/r10k/cli/deploy.rb +108 -0
  5. data/lib/r10k/cli/environment.rb +5 -1
  6. data/lib/r10k/cli/environment/deploy.rb +6 -28
  7. data/lib/r10k/cli/environment/list.rb +6 -10
  8. data/lib/r10k/cli/environment/stale.rb +6 -16
  9. data/lib/r10k/cli/module.rb +5 -1
  10. data/lib/r10k/cli/module/deploy.rb +5 -32
  11. data/lib/r10k/cli/module/list.rb +6 -27
  12. data/lib/r10k/cli/puppetfile.rb +76 -0
  13. data/lib/r10k/cli/synchronize.rb +8 -24
  14. data/lib/r10k/cli/version.rb +22 -0
  15. data/lib/r10k/deployment.rb +55 -26
  16. data/lib/r10k/deployment/config.rb +69 -0
  17. data/lib/r10k/{config → deployment/config}/loader.rb +9 -5
  18. data/lib/r10k/deployment/environment.rb +88 -0
  19. data/lib/r10k/deployment/source.rb +79 -0
  20. data/lib/r10k/errors.rb +3 -5
  21. data/lib/r10k/execution.rb +43 -0
  22. data/lib/r10k/git/cache.rb +131 -0
  23. data/lib/r10k/git/errors.rb +34 -0
  24. data/lib/r10k/git/repository.rb +74 -0
  25. data/lib/r10k/git/working_dir.rb +142 -0
  26. data/lib/r10k/logging.rb +6 -2
  27. data/lib/r10k/module.rb +10 -13
  28. data/lib/r10k/module/forge.rb +35 -22
  29. data/lib/r10k/module/git.rb +18 -8
  30. data/lib/r10k/puppetfile.rb +107 -0
  31. data/lib/r10k/task.rb +13 -0
  32. data/lib/r10k/task/deployment.rb +151 -0
  33. data/lib/r10k/task/environment.rb +29 -0
  34. data/lib/r10k/task/module.rb +18 -0
  35. data/lib/r10k/task/puppetfile.rb +99 -0
  36. data/lib/r10k/task_runner.rb +72 -0
  37. data/lib/r10k/util/purgeable.rb +50 -0
  38. data/lib/r10k/version.rb +1 -1
  39. data/spec/unit/deployment/environment_spec.rb +19 -0
  40. data/spec/unit/git/cache_spec.rb +37 -0
  41. data/spec/unit/git/working_dir_spec.rb +15 -0
  42. data/spec/unit/module/forge_spec.rb +95 -0
  43. data/spec/unit/module_spec.rb +29 -0
  44. metadata +79 -44
  45. data/lib/r10k/action.rb +0 -7
  46. data/lib/r10k/action/environment.rb +0 -74
  47. data/lib/r10k/action/module.rb +0 -36
  48. data/lib/r10k/cli/cache.rb +0 -32
  49. data/lib/r10k/config.rb +0 -46
  50. data/lib/r10k/deployment/environment_collection.rb +0 -75
  51. data/lib/r10k/librarian.rb +0 -31
  52. data/lib/r10k/librarian/dsl.rb +0 -20
  53. data/lib/r10k/root.rb +0 -98
  54. 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
@@ -1,3 +1,3 @@
1
1
  module R10K
2
- VERSION = '0.0.9'
2
+ VERSION = '1.0.0rc1'
3
3
  end
@@ -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