r10k 1.3.5 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +1 -1
  3. data/CHANGELOG.mkd +210 -0
  4. data/CONTRIBUTING.mkd +105 -0
  5. data/Gemfile +2 -6
  6. data/README.mkd +97 -0
  7. data/doc/common-patterns.mkd +44 -0
  8. data/doc/dynamic-environments.mkd +12 -5
  9. data/doc/dynamic-environments/configuration.mkd +16 -1
  10. data/doc/dynamic-environments/{git-environments.markdown → git-environments.mkd} +13 -9
  11. data/doc/dynamic-environments/introduction.mkd +1 -2
  12. data/doc/dynamic-environments/master-configuration.mkd +70 -0
  13. data/doc/dynamic-environments/quickstart.mkd +241 -0
  14. data/doc/dynamic-environments/svn-environments.mkd +45 -0
  15. data/doc/dynamic-environments/usage.mkd +44 -5
  16. data/doc/dynamic-environments/workflow-guide.mkd +247 -0
  17. data/doc/faq.mkd +52 -0
  18. data/doc/puppetfile.mkd +203 -0
  19. data/lib/r10k/action/cri_runner.rb +75 -0
  20. data/lib/r10k/action/deploy.rb +9 -0
  21. data/lib/r10k/action/deploy/display.rb +104 -0
  22. data/lib/r10k/action/deploy/environment.rb +92 -0
  23. data/lib/r10k/action/deploy/module.rb +70 -0
  24. data/lib/r10k/action/puppetfile.rb +10 -0
  25. data/lib/r10k/action/puppetfile/check.rb +41 -0
  26. data/lib/r10k/action/puppetfile/cri_runner.rb +32 -0
  27. data/lib/r10k/action/puppetfile/install.rb +53 -0
  28. data/lib/r10k/action/puppetfile/purge.rb +37 -0
  29. data/lib/r10k/action/runner.rb +36 -0
  30. data/lib/r10k/action/visitor.rb +31 -0
  31. data/lib/r10k/cli/deploy.rb +14 -45
  32. data/lib/r10k/cli/puppetfile.rb +15 -53
  33. data/lib/r10k/deployment.rb +113 -58
  34. data/lib/r10k/deployment/basedir.rb +3 -38
  35. data/lib/r10k/deployment/config.rb +2 -1
  36. data/lib/r10k/deployment/source.rb +2 -0
  37. data/lib/r10k/environment/base.rb +40 -0
  38. data/lib/r10k/environment/git.rb +14 -17
  39. data/lib/r10k/environment/svn.rb +31 -15
  40. data/lib/r10k/errors.rb +33 -22
  41. data/lib/r10k/errors/formatting.rb +28 -0
  42. data/lib/r10k/execution.rb +2 -0
  43. data/lib/r10k/git/cache.rb +1 -6
  44. data/lib/r10k/git/errors.rb +1 -2
  45. data/lib/r10k/git/ref.rb +1 -1
  46. data/lib/r10k/module.rb +1 -1
  47. data/lib/r10k/module/base.rb +94 -2
  48. data/lib/r10k/module/forge.rb +33 -30
  49. data/lib/r10k/module/git.rb +13 -9
  50. data/lib/r10k/module/svn.rb +41 -28
  51. data/lib/r10k/puppetfile.rb +17 -1
  52. data/lib/r10k/semver.rb +2 -0
  53. data/lib/r10k/source/base.rb +8 -0
  54. data/lib/r10k/source/git.rb +1 -1
  55. data/lib/r10k/source/svn.rb +23 -5
  56. data/lib/r10k/svn/remote.rb +23 -3
  57. data/lib/r10k/svn/working_dir.rb +60 -9
  58. data/lib/r10k/task.rb +1 -0
  59. data/lib/r10k/task/deployment.rb +9 -1
  60. data/lib/r10k/task/environment.rb +2 -0
  61. data/lib/r10k/task/module.rb +1 -0
  62. data/lib/r10k/task/puppetfile.rb +3 -0
  63. data/lib/r10k/task_runner.rb +1 -0
  64. data/lib/r10k/util/attempt.rb +84 -0
  65. data/lib/r10k/util/basedir.rb +65 -0
  66. data/lib/r10k/util/purgeable.rb +55 -45
  67. data/lib/r10k/util/setopts.rb +53 -0
  68. data/lib/r10k/util/subprocess.rb +6 -30
  69. data/lib/r10k/util/subprocess/posix/runner.rb +29 -2
  70. data/lib/r10k/util/subprocess/result.rb +17 -4
  71. data/lib/r10k/util/subprocess/subprocess_error.rb +24 -0
  72. data/lib/r10k/version.rb +1 -1
  73. data/r10k.gemspec +7 -29
  74. data/spec/fixtures/unit/puppetfile/invalid-syntax/Puppetfile +1 -0
  75. data/spec/fixtures/unit/puppetfile/load-error/Puppetfile +1 -0
  76. data/spec/matchers/exit_with.rb +28 -0
  77. data/spec/r10k-mocks.rb +3 -0
  78. data/spec/r10k-mocks/mock_config.rb +28 -0
  79. data/spec/r10k-mocks/mock_env.rb +7 -0
  80. data/spec/r10k-mocks/mock_source.rb +10 -0
  81. data/spec/shared-examples/git-ref.rb +7 -7
  82. data/spec/spec_helper.rb +17 -5
  83. data/spec/unit/action/cri_runner_spec.rb +76 -0
  84. data/spec/unit/action/puppetfile/cri_action_spec.rb +65 -0
  85. data/spec/unit/action/runner_spec.rb +64 -0
  86. data/spec/unit/action/visitor_spec.rb +39 -0
  87. data/spec/unit/deployment_spec.rb +142 -0
  88. data/spec/unit/environment/base_spec.rb +38 -0
  89. data/spec/unit/environment/git_spec.rb +40 -10
  90. data/spec/unit/environment/svn_spec.rb +41 -4
  91. data/spec/unit/errors/formatting_spec.rb +84 -0
  92. data/spec/unit/git/alternates_spec.rb +1 -1
  93. data/spec/unit/git/head_spec.rb +1 -1
  94. data/spec/unit/git/ref_spec.rb +1 -1
  95. data/spec/unit/git/working_dir_spec.rb +1 -1
  96. data/spec/unit/module/base_spec.rb +72 -0
  97. data/spec/unit/module/forge_spec.rb +49 -8
  98. data/spec/unit/module/git_spec.rb +78 -0
  99. data/spec/unit/module/svn_spec.rb +40 -4
  100. data/spec/unit/module_spec.rb +3 -3
  101. data/spec/unit/puppetfile_spec.rb +84 -0
  102. data/spec/unit/settings/container_spec.rb +1 -1
  103. data/spec/unit/source/base_spec.rb +31 -0
  104. data/spec/unit/source/git_spec.rb +7 -7
  105. data/spec/unit/source/svn_spec.rb +1 -1
  106. data/spec/unit/svn/working_dir_spec.rb +56 -0
  107. data/spec/unit/util/attempt_spec.rb +82 -0
  108. data/spec/unit/util/setopts_spec.rb +59 -0
  109. data/spec/unit/util/subprocess/result_spec.rb +36 -0
  110. data/spec/unit/util/subprocess/subprocess_error_spec.rb +26 -0
  111. data/spec/unit/util/subprocess_spec.rb +2 -7
  112. metadata +83 -100
  113. data/.nodeset.yml +0 -7
  114. data/.rspec +0 -1
  115. data/README.markdown +0 -276
  116. data/Rakefile +0 -1
  117. data/doc/puppetfile.markdown +0 -87
  118. data/spec/rspec-system-r10k/puppetfile.rb +0 -24
  119. data/spec/rspec-system-r10k/tmpdir.rb +0 -32
  120. data/spec/system-provisioning/el.rb +0 -38
  121. data/spec/system/module/forge/install_spec.rb +0 -51
  122. data/spec/system/module/git/install_spec.rb +0 -117
  123. data/spec/system/module/svn/install_spec.rb +0 -51
  124. data/spec/system/module/svn/update_spec.rb +0 -38
  125. data/spec/system/spec_helper.rb +0 -60
  126. data/spec/system/system-helpers.rb +0 -4
  127. data/spec/system/version_spec.rb +0 -7
@@ -1,16 +1,52 @@
1
1
  require 'r10k/util/subprocess'
2
+ require 'r10k/util/setopts'
2
3
 
3
4
  module R10K
4
5
  module SVN
6
+
7
+ # Manage an SVN working copy.
8
+ #
9
+ # If SVN authentication is required, both username and password must be specified.
10
+ #
11
+ # @api private
12
+ # @since 1.2.0
5
13
  class WorkingDir
6
14
 
7
- # @param full_path [Pathname]
8
- def initialize(full_path)
9
- @full_path = full_path
15
+ include R10K::Util::Setopts
16
+
17
+ # @attribute [r] path
18
+ # @return [Pathname] The full path to the SVN working directory
19
+ # @api private
20
+ attr_reader :path
21
+
22
+ # @!attribute [r] username
23
+ # @return [String, nil] The SVN username, if provided
24
+ # @api private
25
+ attr_reader :username
26
+
27
+ # @!attribute [r] password
28
+ # @return [String, nil] The SVN password, if provided
29
+ # @api private
30
+ attr_reader :password
31
+
32
+ # @param path [Pathname]
33
+ # @param opts [Hash]
34
+ #
35
+ # @option opts [String] :username
36
+ # @option opts [String] :password
37
+ def initialize(path, opts = {})
38
+ @path = path
39
+
40
+ setopts(opts, {:username => :self, :password => :self})
41
+
42
+ if !!(@username) ^ !!(@password)
43
+ raise ArgumentError, "Both username and password must be specified"
44
+ end
10
45
  end
11
46
 
47
+ # Is the directory at this path actually an SVN repository?
12
48
  def is_svn?
13
- dot_svn = @full_path + '.svn'
49
+ dot_svn = @path + '.svn'
14
50
  dot_svn.exist?
15
51
  end
16
52
 
@@ -29,22 +65,37 @@ module R10K
29
65
  def update(revision = nil)
30
66
  argv = %w[update]
31
67
  argv << '-r' << revision if revision
68
+ argv.concat(auth)
32
69
 
33
- svn argv, :cwd => @full_path
70
+ svn(argv, :cwd => @path)
34
71
  end
35
72
 
36
73
  def checkout(url, revision = nil)
37
74
  argv = ['checkout', url]
38
75
  argv << '-r' << revision if revision
39
- argv << @full_path.basename.to_s
76
+ argv << @path.basename.to_s
77
+ argv.concat(auth)
78
+ argv << '-q'
40
79
 
41
- svn argv, :cwd => @full_path.parent
80
+ svn(argv, :cwd => @path.parent)
42
81
  end
43
82
 
44
83
  private
45
84
 
46
85
  def info
47
- svn ["info"], :cwd => @full_path
86
+ argv = %w[info]
87
+ argv.concat(auth)
88
+ svn(argv, :cwd => @path)
89
+ end
90
+
91
+ # Format authentication information for SVN command args, if applicable
92
+ def auth
93
+ auth = []
94
+ if @username
95
+ auth << "--username" << @username
96
+ auth << "--password" << @password
97
+ end
98
+ auth
48
99
  end
49
100
 
50
101
  include R10K::Logging
@@ -58,7 +109,7 @@ module R10K
58
109
  #
59
110
  # @return [String] The stdout from the given command
60
111
  def svn(argv, opts = {})
61
- argv.unshift('svn')
112
+ argv.unshift('svn', '--non-interactive')
62
113
 
63
114
  subproc = R10K::Util::Subprocess.new(argv)
64
115
  subproc.raise_on_fail = true
@@ -1,5 +1,6 @@
1
1
  module R10K
2
2
  module Task
3
+ # @deprecated
3
4
  class Base
4
5
  include R10K::Logging
5
6
 
@@ -4,9 +4,13 @@ require 'r10k/task_runner'
4
4
  require 'r10k/task/environment'
5
5
  require 'r10k/task/puppetfile'
6
6
 
7
+ require 'r10k/util/basedir'
8
+
7
9
  module R10K
8
10
  module Task
9
11
  module Deployment
12
+
13
+ # @deprecated
10
14
  module SharedBehaviors
11
15
 
12
16
  private
@@ -54,6 +58,7 @@ module Deployment
54
58
  end
55
59
  end
56
60
 
61
+ # @deprecated
57
62
  class DeployEnvironments < R10K::Task::Base
58
63
 
59
64
  include SharedBehaviors
@@ -83,6 +88,7 @@ module Deployment
83
88
  end
84
89
  end
85
90
 
91
+ # @deprecated
86
92
  class DeployModules < R10K::Task::Base
87
93
 
88
94
  include SharedBehaviors
@@ -110,6 +116,7 @@ module Deployment
110
116
  end
111
117
  end
112
118
 
119
+ # @deprecated
113
120
  class PurgeEnvironments < R10K::Task::Base
114
121
 
115
122
  def initialize(deployment)
@@ -119,13 +126,14 @@ module Deployment
119
126
 
120
127
  def call
121
128
  @basedirs.each do |path|
122
- basedir = R10K::Deployment::Basedir.new(path,@deployment)
129
+ basedir = R10K::Util::Basedir.from_deployment(path, @deployment)
123
130
  logger.info "Purging stale environments from #{path}"
124
131
  basedir.purge!
125
132
  end
126
133
  end
127
134
  end
128
135
 
136
+ # @deprecated
129
137
  class Display < R10K::Task::Base
130
138
 
131
139
  attr_accessor :puppetfile
@@ -4,6 +4,8 @@ require 'r10k/task/puppetfile'
4
4
  module R10K
5
5
  module Task
6
6
  module Environment
7
+
8
+ # @deprecated
7
9
  class Deploy < R10K::Task::Base
8
10
 
9
11
  attr_writer :update_puppetfile
@@ -3,6 +3,7 @@ require 'r10k/task'
3
3
  module R10K
4
4
  module Task
5
5
  module Module
6
+ # @deprecated
6
7
  class Sync < R10K::Task::Base
7
8
  def initialize(mod)
8
9
  @mod = mod
@@ -6,6 +6,7 @@ require 'r10k/task_runner'
6
6
  module R10K
7
7
  module Task
8
8
  module Puppetfile
9
+ # @deprecated
9
10
  class Sync < R10K::Task::Base
10
11
  def initialize(puppetfile)
11
12
  @puppetfile = puppetfile
@@ -25,6 +26,7 @@ module Puppetfile
25
26
  end
26
27
  end
27
28
 
29
+ # @deprecated
28
30
  class DeployModules < R10K::Task::Base
29
31
 
30
32
  attr_accessor :module_names
@@ -73,6 +75,7 @@ module Puppetfile
73
75
  end
74
76
  end
75
77
 
78
+ # @deprecated
76
79
  class Purge < R10K::Task::Base
77
80
  def initialize(puppetfile)
78
81
  @puppetfile = puppetfile
@@ -1,6 +1,7 @@
1
1
  require 'r10k/task'
2
2
 
3
3
  module R10K
4
+ # @deprecated
4
5
  class TaskRunner
5
6
 
6
7
  include R10K::Logging
@@ -0,0 +1,84 @@
1
+ require 'r10k/logging'
2
+ require 'r10k/errors/formatting'
3
+ require 'r10k/util/setopts'
4
+ require 'colored'
5
+
6
+ module R10K
7
+ module Util
8
+
9
+ # Attempt a series of dependent nested tasks and cleanly handle errors.
10
+ #
11
+ # @api private
12
+ class Attempt
13
+
14
+ include R10K::Logging
15
+ include R10K::Util::Setopts
16
+
17
+ # @!attribute [r] status
18
+ # @return [Symbol] The status of this task
19
+ attr_reader :status
20
+
21
+ def initialize(initial, opts = {})
22
+ @initial = initial
23
+ @tries = []
24
+ @status = :notrun
25
+ setopts(opts, {:trace => :self})
26
+ end
27
+
28
+ # Run this attempt to completion.
29
+ #
30
+ # @todo determine the structure of the ret
31
+ # @return [Object] The aggregate result of all work performed.
32
+ def run
33
+ @status = :running
34
+ result = apply(@initial, @tries)
35
+ @status = :ok if @status == :running
36
+ result
37
+ end
38
+
39
+ # Add another action to take for this attempt
40
+ #
41
+ # @yieldparam [Object] The result of the previous action.
42
+ # @yieldreturn [Object, Array<Object>, NilClass] The result of this action.
43
+ # If the value is an object, it will be passed to the next attempt. If
44
+ # the value is an Array then each element will be individually passed
45
+ # to the next try. If the value is false or nil then no further action
46
+ # will be taken.
47
+ def try(&block)
48
+ @tries << block
49
+ self
50
+ end
51
+
52
+ def ok?
53
+ @status == :ok
54
+ end
55
+
56
+ private
57
+
58
+ def apply(input, tries)
59
+ return input if tries.empty?
60
+
61
+ case input
62
+ when Array
63
+ apply_all(input, tries)
64
+ when NilClass, FalseClass
65
+ input
66
+ else
67
+ apply_one(input, tries)
68
+ end
69
+ end
70
+
71
+ def apply_all(values, tries)
72
+ values.map { |v| apply_one(v, tries) }
73
+ end
74
+
75
+ def apply_one(value, tries)
76
+ apply(tries.first.call(value), tries.drop(1))
77
+ rescue => e
78
+ @status = :failed
79
+ logger.error R10K::Errors::Formatting.format_exception(e, @trace)
80
+ e
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,65 @@
1
+ require 'r10k/deployment'
2
+ require 'r10k/logging'
3
+ require 'r10k/util/purgeable'
4
+
5
+ module R10K
6
+ module Util
7
+
8
+ # Represents a directory that can purge unmanaged contents
9
+ #
10
+ # @todo pick a better name than basedir. Expect this class to be renamed.
11
+ #
12
+ # @api private
13
+ class Basedir
14
+
15
+ include R10K::Util::Purgeable
16
+ include R10K::Logging
17
+
18
+ # Create a new Basedir by selecting sources from a deployment that match
19
+ # the specified path.
20
+ #
21
+ # @param path [String]
22
+ # @param deployment [R10K::Deployment]
23
+ #
24
+ # @return [R10K::Util::Basedir]
25
+ def self.from_deployment(path, deployment)
26
+ sources = deployment.sources.select { |source| source.managed_directory == path }
27
+ new(path, sources)
28
+ end
29
+
30
+ # @param path [String] The path to the directory to manage
31
+ # @param sources [Array<R10K::Util::Purgeable>] A list of purgeable objects
32
+ def initialize(path, sources)
33
+ if sources.is_a? R10K::Deployment
34
+ raise ArgumentError, "Expected Array<Purgeable>, got R10K::Deployment"
35
+ end
36
+ @path = path
37
+ @sources = sources
38
+ end
39
+
40
+ # Return the path of the basedir
41
+ # @note This implements a required method for the Purgeable mixin
42
+ # @return [String]
43
+ def managed_directory
44
+ @path
45
+ end
46
+
47
+ # List all environments that should exist in this basedir
48
+ # @note This implements a required method for the Purgeable mixin
49
+ # @return [Array<String>]
50
+ def desired_contents
51
+ @sources.map(&:desired_contents).flatten
52
+ end
53
+
54
+ def purge!
55
+ @sources.each do |source|
56
+ logger.debug1 "Source #{source.name} in #{@path} claimed contents #{source.desired_contents.inspect}"
57
+ end
58
+ if !stale_contents.empty?
59
+ logger.debug "No sources in #{@path} claimed contents #{stale_contents.inspect}"
60
+ end
61
+ super
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,50 +1,60 @@
1
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
2
 
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
3
+ module R10K
4
+ module Util
5
+
6
+ # Mixin for purging stale directory contents.
7
+ #
8
+ # @abstract Classes using this mixin need to implement {#managed_directory} and
9
+ # {#desired_contents}
10
+ module Purgeable
11
+
12
+ # @!method logger
13
+ # @abstract Including classes must provide a logger method
14
+ # @return [Log4r::Logger]
15
+
16
+ # @!method desired_contents
17
+ # @abstract Including classes must implement this method to list the
18
+ # expected filenames of managed_directory
19
+ # @return [Array<String>] A list of directory contents that should be present
20
+
21
+ # @!method managed_directory
22
+ # @abstract Including classes must implement this method to return the
23
+ # path to the directory that can be purged
24
+ # @return [String] The path to the directory to be purged
25
+
26
+ # @return [Array<String>] The present directory entries in `self.managed_directory`
27
+ def current_contents
28
+ dir = self.managed_directory
29
+ glob_exp = File.join(dir, '*')
30
+
31
+ Dir.glob(glob_exp).map do |fname|
32
+ File.basename(fname)
33
+ end
34
+ end
35
+
36
+ # @return [Array<String>] Directory contents that are expected but not present
37
+ def pending_contents
38
+ desired_contents - current_contents
39
+ end
40
+
41
+ # @return [Array<String>] Directory contents that are present but not expected
42
+ def stale_contents
43
+ current_contents - desired_contents
44
+ end
45
+
46
+ # Forcibly remove all unmanaged content in `self.managed_directory`
47
+ def purge!
48
+ if stale_contents.empty?
49
+ logger.debug1 "No stale contents in #{managed_directory}, nothing to purge"
50
+ else
51
+ stale_contents.each do |fname|
52
+ fpath = File.join(self.managed_directory, fname)
53
+ logger.debug "Removing stale path #{fpath}"
54
+ FileUtils.rm_rf(fpath, :secure => true)
55
+ end
56
+ end
57
+ end
45
58
  end
46
59
  end
47
-
48
- end
49
- end
50
60
  end