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