r10k 1.2.4 → 1.3.0rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +8 -8
  2. data/{CHANGELOG → CHANGELOG.mkd} +51 -41
  3. data/doc/dynamic-environments/configuration.mkd +1 -1
  4. data/doc/dynamic-environments/git-environments.markdown +19 -0
  5. data/doc/dynamic-environments/usage.mkd +6 -0
  6. data/lib/r10k/cli/deploy.rb +15 -0
  7. data/lib/r10k/cli/ext/logging.rb +0 -1
  8. data/lib/r10k/cli/module/deploy.rb +0 -1
  9. data/lib/r10k/cli/puppetfile.rb +2 -2
  10. data/lib/r10k/cli.rb +2 -16
  11. data/lib/r10k/deployment/environment.rb +9 -79
  12. data/lib/r10k/deployment/source.rb +15 -89
  13. data/lib/r10k/deployment.rb +13 -14
  14. data/lib/r10k/environment/base.rb +42 -0
  15. data/lib/r10k/environment/git.rb +79 -0
  16. data/lib/r10k/environment/svn.rb +73 -0
  17. data/lib/r10k/environment.rb +7 -0
  18. data/lib/r10k/execution.rb +0 -1
  19. data/lib/r10k/git/cache.rb +11 -5
  20. data/lib/r10k/git/repository.rb +1 -8
  21. data/lib/r10k/git/working_dir.rb +11 -34
  22. data/lib/r10k/git.rb +0 -1
  23. data/lib/r10k/instance_cache.rb +32 -0
  24. data/lib/r10k/keyed_factory.rb +39 -0
  25. data/lib/r10k/module/forge.rb +2 -3
  26. data/lib/r10k/module/svn.rb +0 -1
  27. data/lib/r10k/puppetfile.rb +0 -1
  28. data/lib/r10k/registry.rb +3 -31
  29. data/lib/r10k/source/base.rb +60 -0
  30. data/lib/r10k/source/git.rb +195 -0
  31. data/lib/r10k/source/svn.rb +140 -0
  32. data/lib/r10k/source.rb +39 -0
  33. data/lib/r10k/svn/remote.rb +48 -0
  34. data/lib/r10k/svn/working_dir.rb +0 -2
  35. data/lib/r10k/svn.rb +6 -0
  36. data/lib/r10k/task/deployment.rb +1 -2
  37. data/lib/r10k/task.rb +0 -2
  38. data/lib/r10k/task_runner.rb +0 -1
  39. data/lib/r10k/util/core_ext/hash_ext.rb +19 -0
  40. data/lib/r10k/util/subprocess.rb +0 -1
  41. data/lib/r10k/version.rb +1 -1
  42. data/lib/r10k.rb +1 -0
  43. data/spec/unit/deployment/environment_spec.rb +16 -15
  44. data/spec/unit/environment/git_spec.rb +81 -0
  45. data/spec/unit/environment/svn_spec.rb +76 -0
  46. data/spec/unit/git/repository_spec.rb +0 -10
  47. data/spec/unit/git/working_dir_spec.rb +1 -110
  48. data/spec/unit/{registry_spec.rb → instance_cache_spec.rb} +3 -3
  49. data/spec/unit/keyed_factory_spec.rb +51 -0
  50. data/spec/unit/source/git_spec.rb +274 -0
  51. data/spec/unit/source/svn_spec.rb +102 -0
  52. data/spec/unit/source_spec.rb +10 -0
  53. data/spec/unit/svn/remote_spec.rb +21 -0
  54. data/spec/unit/util/core_ext/hash_ext_spec.rb +63 -0
  55. metadata +36 -10
  56. data/lib/r10k/git/alternates.rb +0 -49
  57. data/spec/unit/git/alternates_spec.rb +0 -90
@@ -0,0 +1,79 @@
1
+ require 'r10k/logging'
2
+ require 'r10k/puppetfile'
3
+ require 'r10k/git/working_dir'
4
+
5
+ # This class implements an environment based on a Git branch.
6
+ #
7
+ # @since 1.3.0
8
+ class R10K::Environment::Git < R10K::Environment::Base
9
+
10
+ include R10K::Logging
11
+
12
+ # @!attribute [r] remote
13
+ # @return [String] The URL to the remote git repository
14
+ attr_reader :remote
15
+
16
+ # @!attribute [r] ref
17
+ # @return [String] The git reference to use for this environment
18
+ attr_reader :ref
19
+
20
+ # @!attribute [r] working_dir
21
+ # @api private
22
+ # @return [R10K::Git::WorkingDir] The git working directory backing this environment
23
+ attr_reader :working_dir
24
+
25
+ # @!attribute [r] puppetfile
26
+ # @api public
27
+ # @return [R10K::Puppetfile] The puppetfile instance associated with this environment
28
+ attr_reader :puppetfile
29
+
30
+ # Initialize the given SVN environment.
31
+ #
32
+ # @param name [String] The unique name describing this environment.
33
+ # @param basedir [String] The base directory where this environment will be created.
34
+ # @param dirname [String] The directory name for this environment.
35
+ # @param options [Hash] An additional set of options for this environment.
36
+ #
37
+ # @param options [String] :remote The URL to the remote git repository
38
+ # @param options [String] :ref The git reference to use for this environment
39
+ def initialize(name, basedir, dirname, options = {})
40
+ super
41
+ @remote = options[:remote]
42
+ @ref = options[:ref]
43
+
44
+ @working_dir = R10K::Git::WorkingDir.new(@ref, @remote, @basedir, @dirname)
45
+ @puppetfile = R10K::Puppetfile.new(@full_path)
46
+ end
47
+
48
+ # Clone or update the given Git environment.
49
+ #
50
+ # If the environment is being created for the first time, it will
51
+ # automatically update all modules to ensure that the environment is complete.
52
+ #
53
+ # @api public
54
+ # @return [void]
55
+ def sync
56
+ recursive_needed = !(@working_dir.cloned?)
57
+ @working_dir.sync
58
+
59
+ if recursive_needed
60
+ logger.debug "Environment #{@full_path} is a fresh clone; automatically updating modules."
61
+ sync_modules
62
+ end
63
+ end
64
+
65
+ # @api private
66
+ def sync_modules
67
+ modules.each do |mod|
68
+ logger.debug "Deploying module #{mod.name}"
69
+ mod.sync
70
+ end
71
+ end
72
+
73
+ # @return [Array<R10K::Module::Base>] All modules defined in the Puppetfile
74
+ # associated with this environment.
75
+ def modules
76
+ @puppetfile.load
77
+ @puppetfile.modules
78
+ end
79
+ end
@@ -0,0 +1,73 @@
1
+ require 'r10k/puppetfile'
2
+ require 'r10k/svn/working_dir'
3
+
4
+ # This class implements an environment based on an SVN branch.
5
+ #
6
+ # @since 1.3.0
7
+ class R10K::Environment::SVN < R10K::Environment::Base
8
+
9
+ include R10K::Logging
10
+
11
+ # @!attribute [r] remote
12
+ # @return [String] The URL to the remote SVN branch to check out
13
+ attr_reader :remote
14
+
15
+ # @!attribute [r] working_dir
16
+ # @api private
17
+ # @return [R10K::SVN::WorkingDir] The SVN working directory backing this environment
18
+ attr_reader :working_dir
19
+
20
+ # @!attribute [r] puppetfile
21
+ # @api public
22
+ # @return [R10K::Puppetfile] The puppetfile instance associated with this environment
23
+ attr_reader :puppetfile
24
+
25
+ # Initialize the given SVN environment.
26
+ #
27
+ # @param name [String] The unique name describing this environment.
28
+ # @param basedir [String] The base directory where this environment will be created.
29
+ # @param dirname [String] The directory name for this environment.
30
+ # @param options [Hash] An additional set of options for this environment.
31
+ #
32
+ # @param options [String] :remote The URL to the remote SVN branch to check out
33
+ def initialize(name, basedir, dirname, options = {})
34
+ super
35
+
36
+ @remote = options[:remote]
37
+
38
+ @working_dir = R10K::SVN::WorkingDir.new(Pathname.new(@full_path))
39
+ @puppetfile = R10K::Puppetfile.new(@full_path)
40
+ end
41
+
42
+ # Perform an initial checkout of the SVN repository or update the repository.
43
+ #
44
+ # If the environment is being created for the first time, it will
45
+ # automatically update all modules to ensure that the environment is complete.
46
+ #
47
+ # @api public
48
+ # @return [void]
49
+ def sync
50
+ if @working_dir.is_svn?
51
+ @working_dir.update
52
+ else
53
+ @working_dir.checkout(@remote)
54
+ logger.debug "Environment #{@full_path} is a fresh clone; automatically updating modules."
55
+ sync_modules
56
+ end
57
+ end
58
+
59
+ # @return [Array<R10K::Module::Base>] All modules defined in the Puppetfile
60
+ # associated with this environment.
61
+ def modules
62
+ @puppetfile.load
63
+ @puppetfile.modules
64
+ end
65
+
66
+ # @api private
67
+ def sync_modules
68
+ modules.each do |mod|
69
+ logger.debug "Deploying module #{mod.name}"
70
+ mod.sync
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,7 @@
1
+ module R10K
2
+ module Environment
3
+ require 'r10k/environment/base'
4
+ require 'r10k/environment/git'
5
+ require 'r10k/environment/svn'
6
+ end
7
+ end
@@ -1,4 +1,3 @@
1
- require 'r10k/logging'
2
1
  require 'r10k/errors'
3
2
 
4
3
  require 'systemu'
@@ -1,5 +1,3 @@
1
- require 'r10k/logging'
2
-
3
1
  require 'r10k/git'
4
2
  require 'r10k/git/repository'
5
3
 
@@ -15,12 +13,20 @@ class R10K::Git::Cache < R10K::Git::Repository
15
13
 
16
14
  def_setting_attr :cache_root, File.expand_path(ENV['HOME'] ? '~/.r10k/git': '/root/.r10k/git')
17
15
 
18
- def self.registry
19
- @registry ||= R10K::Registry.new(self)
16
+ # Lazily construct an instance cache for R10K::Git::Cache objects
17
+ # @api private
18
+ def self.instance_cache
19
+ @instance_cache ||= R10K::InstanceCache.new(self)
20
20
  end
21
21
 
22
+ # Generate a new instance with the given remote or return an existing object
23
+ # with the given remote. This should be used over R10K::Git::Cache.new.
24
+ #
25
+ # @api public
26
+ # @param remote [String] The git remote to cache
27
+ # @return [R10K::Git::Cache] The requested cache object.
22
28
  def self.generate(remote)
23
- registry.generate(remote)
29
+ instance_cache.generate(remote)
24
30
  end
25
31
 
26
32
  include R10K::Logging
@@ -84,7 +84,7 @@ class R10K::Git::Repository
84
84
 
85
85
  ret = {}
86
86
  output.stdout.each_line do |line|
87
- next if line.match(/\(push\)/)
87
+ next if line.match /\(push\)/
88
88
  name, url, _ = line.split(/\s+/)
89
89
  ret[name] = url
90
90
  end
@@ -92,13 +92,6 @@ class R10K::Git::Repository
92
92
  ret
93
93
  end
94
94
 
95
- def tags
96
- entries = []
97
- output = git(['tag', '-l'], :git_dir => @git_dir).stdout
98
- output.each_line { |line| entries << line.chomp }
99
- entries
100
- end
101
-
102
95
  private
103
96
 
104
97
  # Fetch objects and refs from the given git remote
@@ -1,5 +1,4 @@
1
1
  require 'forwardable'
2
- require 'r10k/logging'
3
2
  require 'r10k/git'
4
3
  require 'r10k/git/cache'
5
4
 
@@ -41,8 +40,7 @@ class R10K::Git::WorkingDir < R10K::Git::Repository
41
40
  @full_path = File.join(@basedir, @dirname)
42
41
  @git_dir = File.join(@full_path, '.git')
43
42
 
44
- @alternates = R10K::Git::Alternates.new(@git_dir)
45
- @cache = R10K::Git::Cache.generate(@remote)
43
+ @cache = R10K::Git::Cache.generate(@remote)
46
44
 
47
45
  if ref.is_a? String
48
46
  @ref = R10K::Git::Ref.new(ref, self)
@@ -62,9 +60,7 @@ class R10K::Git::WorkingDir < R10K::Git::Repository
62
60
  end
63
61
 
64
62
  def update
65
- update_remotes if update_remotes?
66
-
67
- if ref_needs_fetch?
63
+ if fetch?
68
64
  fetch_from_cache
69
65
  checkout(@ref)
70
66
  elsif needs_checkout?
@@ -117,17 +113,22 @@ class R10K::Git::WorkingDir < R10K::Git::Repository
117
113
 
118
114
  private
119
115
 
120
- # Do we need to fetch additional objects and refs in order to resolve the given ref?
121
- # @return [true, false]
122
- def ref_needs_fetch?
116
+ def fetch?
123
117
  @ref.fetch?
124
118
  end
125
119
 
126
120
  def fetch_from_cache
121
+ set_cache_remote
127
122
  @cache.sync
128
123
  fetch('cache')
129
124
  end
130
125
 
126
+ def set_cache_remote
127
+ if self.remote != @cache.remote
128
+ git ["remote", "set-url", "cache", @cache.git_dir], :path => @full_path
129
+ end
130
+ end
131
+
131
132
  # Perform a non-bare clone of a git repository.
132
133
  def clone
133
134
  @cache.sync
@@ -149,30 +150,6 @@ class R10K::Git::WorkingDir < R10K::Git::Repository
149
150
  expected = ref.sha1
150
151
  actual = rev_parse('HEAD')
151
152
 
152
- !(expected == actual)
153
- end
154
-
155
- def update_remotes?
156
- real_remotes = remotes
157
-
158
- expected_origin = @remote
159
- expected_cache = @cache.git_dir
160
-
161
- !(expected_origin == real_remotes['origin'] and expected_cache == real_remotes['cache'])
162
- end
163
-
164
- def update_remotes
165
- # todo: remove all existing refs as they may belong to the old remote
166
- git ['remote', 'set-url', 'origin', remote], :path => @full_path
167
- git ['remote', 'set-url', 'cache', @cache.git_dir], :path => @full_path
168
- @alternates << File.join(@cache.git_dir, 'objects')
169
- logger.debug("Removing stale git tags from #{@full_path}")
170
- remove_tags
171
- end
172
-
173
- def remove_tags
174
- tags.each do |tag|
175
- git ['tag', '-d', tag], :path => @full_path
176
- end
153
+ ! (expected == actual)
177
154
  end
178
155
  end
data/lib/r10k/git.rb CHANGED
@@ -10,7 +10,6 @@ module R10K
10
10
 
11
11
  require 'r10k/git/repository'
12
12
  require 'r10k/git/cache'
13
- require 'r10k/git/alternates'
14
13
  require 'r10k/git/working_dir'
15
14
  end
16
15
  end
@@ -0,0 +1,32 @@
1
+ module R10K
2
+
3
+ # This class implements a generic object memoization container. It caches
4
+ # new objects and returns cached objects based on the instantiation arguments.
5
+ class InstanceCache
6
+
7
+ # Initialize a new registry with a given class
8
+ #
9
+ # @param klass [Class] The class to memoize
10
+ # @param method [Symbol] The method name to use when creating objects.
11
+ # Defaults to :new.
12
+ def initialize(klass, method = :new)
13
+ @klass = klass
14
+ @method = method
15
+ @instances = {}
16
+ end
17
+
18
+ # Create a new object, or return a memoized object.
19
+ #
20
+ # @param args [*Object] The arguments to pass to the initialize method
21
+ #
22
+ # @return [Object] A memoized instance of the registered class
23
+ def generate(*args)
24
+ @instances[args] ||= @klass.send(@method, *args)
25
+ end
26
+
27
+ # Clear all memoized objects
28
+ def clear!
29
+ @instances = {}
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ module R10K
2
+
3
+ # This implements a factory by storing classes indexed with a given key and
4
+ # creates objects based on that key.
5
+ class KeyedFactory
6
+
7
+ # @!attribute [r] implementations
8
+ # @return [Hash<Object, Class>] A hash of keys and the associated
9
+ # implementations that this factory can generate.
10
+ attr_reader :implementations
11
+
12
+ def initialize
13
+ @implementations = {}
14
+ end
15
+
16
+ def register(key, klass)
17
+ if @implementations.has_key?(key)
18
+ raise DuplicateImplementationError, "Class already registered for #{key}"
19
+ else
20
+ @implementations[key] = klass
21
+ end
22
+ end
23
+
24
+ def retrieve(key)
25
+ @implementations[key]
26
+ end
27
+
28
+ def generate(key, *args)
29
+ if (impl = @implementations[key])
30
+ impl.new(*args)
31
+ else
32
+ raise UnknownImplementationError, "No class registered for #{key}"
33
+ end
34
+ end
35
+
36
+ class DuplicateImplementationError < StandardError; end
37
+ class UnknownImplementationError < StandardError; end
38
+ end
39
+ end
@@ -1,6 +1,5 @@
1
1
  require 'r10k/module'
2
2
  require 'r10k/errors'
3
- require 'r10k/logging'
4
3
  require 'r10k/module/metadata'
5
4
  require 'r10k/util/subprocess'
6
5
  require 'r10k/module_repository/forge'
@@ -126,7 +125,7 @@ class R10K::Module::Forge < R10K::Module::Base
126
125
  cmd = []
127
126
  cmd << 'install'
128
127
  cmd << "--version=#{expected_version}" if expected_version
129
- cmd << "--force"
128
+ cmd << "--ignore-dependencies"
130
129
  cmd << @full_name
131
130
  pmt cmd
132
131
  end
@@ -135,7 +134,7 @@ class R10K::Module::Forge < R10K::Module::Base
135
134
  cmd = []
136
135
  cmd << 'upgrade'
137
136
  cmd << "--version=#{expected_version}" if expected_version
138
- cmd << "--force"
137
+ cmd << "--ignore-dependencies"
139
138
  cmd << @full_name
140
139
  pmt cmd
141
140
  end
@@ -1,6 +1,5 @@
1
1
  require 'r10k/module'
2
2
  require 'r10k/execution'
3
- require 'r10k/logging'
4
3
  require 'r10k/svn/working_dir'
5
4
 
6
5
  class R10K::Module::SVN < R10K::Module::Base
@@ -1,5 +1,4 @@
1
1
  require 'r10k/module'
2
- require 'r10k/logging'
3
2
  require 'r10k/util/purgeable'
4
3
 
5
4
  module R10K
data/lib/r10k/registry.rb CHANGED
@@ -1,32 +1,4 @@
1
- module R10K
1
+ require 'r10k/instance_cache'
2
2
 
3
- # R10K::Registry implements a generic object memoization container. It caches
4
- # new objects and returns cached objects based on the instantiation arguments.
5
- class Registry
6
-
7
- # Initialize a new registry with a given class
8
- #
9
- # @param klass [Class] The class to memoize
10
- # @param method [Symbol] The method name to use when creating objects.
11
- # Defaults to :new.
12
- def initialize(klass, method = :new)
13
- @klass = klass
14
- @method = method
15
- @instances = {}
16
- end
17
-
18
- # Create a new object, or return a memoized object.
19
- #
20
- # @param args [*Object] The arguments to pass to the initialize method
21
- #
22
- # @return [Object] A memoized instance of the registered class
23
- def generate(*args)
24
- @instances[args] ||= @klass.send(@method, *args)
25
- end
26
-
27
- # Clear all memoized objects
28
- def clear!
29
- @instances = {}
30
- end
31
- end
32
- end
3
+ # @deprecated
4
+ R10K::Registry = R10K::InstanceCache
@@ -0,0 +1,60 @@
1
+ # This class defines a common interface for source implementations.
2
+ #
3
+ # @since 1.3.0
4
+ class R10K::Source::Base
5
+
6
+ # @!attribute [r] basedir
7
+ # @return [String] The path this source will place environments in
8
+ attr_reader :basedir
9
+
10
+ # @!attribute [r] name
11
+ # @return [String] The short name for this environment source
12
+ attr_reader :name
13
+
14
+ # @!attribute [r] prefix
15
+ # @return [true, false] Whether the source name should be prefixed to each
16
+ # environment basedir. Defaults to false
17
+ attr_reader :prefix
18
+
19
+ # Initialize the given source.
20
+ #
21
+ # @param name [String] The identifier for this source.
22
+ # @param basedir [String] The base directory where the generated environments will be created.
23
+ # @param options [Hash] An additional set of options for this source. The
24
+ # semantics of this hash may depend on the source implementation.
25
+ #
26
+ # @option options [Boolean] :prefix Whether to prefix the source name to the
27
+ # environment directory names. All sources should respect this option.
28
+ # Defaults to false.
29
+ def initialize(name, basedir, options = {})
30
+ @name = name
31
+ @basedir = basedir
32
+ @prefix = options.delete(:prefix)
33
+ @options = options
34
+ end
35
+
36
+ # Perform any actions needed for loading environments that may have side
37
+ # effects.
38
+ #
39
+ # Actions done during preloading may include things like updating caches or
40
+ # performing network queries. If an environment has not been preloaded but
41
+ # {#environments} is invoked, it should return the best known state of
42
+ # environments or return an empty list.
43
+ #
44
+ # @api public
45
+ # @abstract
46
+ # @return [void]
47
+ def preload!
48
+
49
+ end
50
+
51
+ # Enumerate the environments associated with this SVN source.
52
+ #
53
+ # @api public
54
+ # @abstract
55
+ # @return [Array<R10K::Environment::Base>] An array of environments created
56
+ # from this source.
57
+ def environments
58
+ raise NotImplementedError, "#{self.class} has not implemented method #{__method__}"
59
+ end
60
+ end