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.
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