r10k 1.2.4 → 1.3.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 (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