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
data/lib/r10k/module.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  require 'r10k'
2
2
 
3
- class R10K::Module
3
+ module R10K::Module
4
4
 
5
5
  # Register an inheriting class for later generation
6
- def self.inherited(klass)
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] path The root path to install the module in
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, path, args)
24
- if implementation = @klasses.find { |klass| klass.implements(name, args) }
25
- obj = implementation.send(:allocate)
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, :path
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(@path, @name)
37
+ File.join(@basedir, @name)
41
38
  end
42
39
  end
43
40
 
@@ -7,70 +7,81 @@ require 'systemu'
7
7
  require 'semver'
8
8
  require 'json'
9
9
 
10
- class R10K::Module::Forge < R10K::Module
10
+ module R10K
11
+ module Module
12
+ class Forge
11
13
 
12
- def self.implements(name, args)
13
- args.is_a? String and SemVer.valid?(args)
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
- def initialize(name, path, args)
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
- @version = SemVer.new(@args)
29
+
30
+ if args.is_a? String
31
+ @version = SemVer.new(args)
32
+ end
25
33
  end
26
34
 
27
- def sync!(options = {})
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
- private
52
-
53
- def current_version
54
- SemVer.new(metadata['version'])
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 == current_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 '#{@path}' #{args.join(' ')}"
73
- log_event = "puppet module #{args.join(' ')}, modulepath: #{@path.inspect}"
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
@@ -1,24 +1,34 @@
1
1
  require 'r10k'
2
2
  require 'r10k/module'
3
- require 'r10k/synchro/git'
3
+ require 'r10k/git/working_dir'
4
+ require 'forwardable'
4
5
 
5
- class R10K::Module::Git < R10K::Module
6
+ module R10K
7
+ module Module
8
+ class Git
9
+ include R10K::Module
6
10
 
7
- def self.implements(name, args)
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
- def initialize(name, path, args)
14
- super
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 sync!(options = {})
21
- synchro = R10K::Synchro::Git.new(@remote)
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,13 @@
1
+ require 'r10k/logging'
2
+
3
+ module R10K
4
+ module Task
5
+ class Base
6
+ include R10K::Logging
7
+
8
+ # @!attribute [r] task_runner
9
+ # @return [R10K::TaskRunner] The task runner that's executing this command
10
+ attr_accessor :task_runner
11
+ end
12
+ end
13
+ end
@@ -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
+