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
@@ -0,0 +1,37 @@
1
+ require 'r10k/puppetfile'
2
+ require 'r10k/util/setopts'
3
+ require 'r10k/errors/formatting'
4
+ require 'r10k/logging'
5
+
6
+ module R10K
7
+ module Action
8
+ module Puppetfile
9
+ class Purge
10
+ include R10K::Logging
11
+ include R10K::Util::Setopts
12
+
13
+ def initialize(opts, argv)
14
+ @opts = opts
15
+ @argv = argv
16
+
17
+ setopts(opts, {
18
+ :root => :self,
19
+ :moduledir => :self,
20
+ :puppetfile => :path,
21
+ :trace => :self,
22
+ })
23
+ end
24
+
25
+ def call
26
+ pf = R10K::Puppetfile.new(@root, @moduledir, @path)
27
+ pf.load
28
+ pf.purge!
29
+ true
30
+ rescue => e
31
+ logger.error R10K::Errors::Formatting.format_exception(e, opts[:trace])
32
+ false
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ module R10K
2
+ module Action
3
+ class Runner
4
+ def initialize(opts, argv, klass)
5
+ @opts = opts
6
+ @argv = argv
7
+ @klass = klass
8
+ end
9
+
10
+ def instance
11
+ if @_instance.nil?
12
+ iopts = @opts.dup
13
+ iopts.delete(:loglevel)
14
+ @_instance = @klass.new(iopts, @argv)
15
+ end
16
+ @_instance
17
+ end
18
+
19
+ def call
20
+ setup_logging
21
+ setup_settings
22
+ # @todo check arguments
23
+ instance.call
24
+ end
25
+
26
+ def setup_logging
27
+ if @opts.key?(:loglevel)
28
+ R10K::Logging.level = @opts[:loglevel]
29
+ end
30
+ end
31
+
32
+ def setup_settings
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ require 'r10k/errors/formatting'
2
+
3
+ module R10K
4
+ module Action
5
+ # Implement the Visitor pattern via pseudo double dispatch.
6
+ #
7
+ # Visitor classes must implement #visit_type methods for each type that may
8
+ # be visited. If the visitor should descend into child objects the #visit_
9
+ # method should yield to the passed block.
10
+ #
11
+ # Visitor classes must implement #logger so that error messages can be logged.
12
+ #
13
+ # @api private
14
+ module Visitor
15
+
16
+ # Dispatch to the type specific visitor method
17
+ #
18
+ # @param type [Symbol] The object type to dispatch for
19
+ # @param other [Object] The actual object to pass to the visitor method
20
+ # @param block [Proc] The block that the called visitor method may yield
21
+ # to in case recursion is desired.
22
+ # @return [void]
23
+ def visit(type, other, &block)
24
+ send("visit_#{type}", other, &block)
25
+ rescue => e
26
+ logger.error R10K::Errors::Formatting.format_exception(e, @trace)
27
+ @visit_ok = false
28
+ end
29
+ end
30
+ end
31
+ end
@@ -5,6 +5,10 @@ require 'r10k/deployment/config'
5
5
  require 'r10k/task_runner'
6
6
  require 'r10k/task/deployment'
7
7
 
8
+ require 'r10k/action/cri_runner'
9
+ require 'r10k/action/deploy'
10
+
11
+
8
12
  require 'cri'
9
13
 
10
14
  module R10K::CLI
@@ -52,26 +56,10 @@ scheduled. On subsequent deployments, Puppetfile deployment will default to off.
52
56
 
53
57
  flag :p, :puppetfile, 'Deploy modules from a puppetfile'
54
58
 
55
- run do |opts, args, cmd|
56
- deploy = R10K::Deployment.load_config(opts[:config])
57
-
58
- task = R10K::Task::Deployment::DeployEnvironments.new(deploy)
59
- task.update_puppetfile = opts[:puppetfile]
60
- task.environment_names = args
61
-
62
- purge = R10K::Task::Deployment::PurgeEnvironments.new(deploy)
63
-
64
- runner = R10K::TaskRunner.new(:trace => opts[:trace])
65
- runner.append_task task
66
- runner.append_task purge
67
- runner.run
68
-
69
- exit runner.exit_value
70
- end
59
+ runner R10K::Action::CriRunner.wrap(R10K::Action::Deploy::Environment)
71
60
  end
72
61
  end
73
62
  end
74
- self.command.add_command(Environment.command)
75
63
 
76
64
  module Module
77
65
  def self.command
@@ -88,49 +76,30 @@ try to deploy the given module names in all environments.
88
76
 
89
77
  required :e, :environment, 'Update the modules in the given environment'
90
78
 
91
- run do |opts, args, cmd|
92
- deploy = R10K::Deployment.load_config(opts[:config])
93
-
94
- task = R10K::Task::Deployment::DeployModules.new(deploy)
95
- task.module_names = args
96
- task.environment_names = [opts[:environment]] if opts[:environment]
97
-
98
- runner = R10K::TaskRunner.new(:trace => opts[:trace])
99
- runner.append_task task
100
- runner.run
101
-
102
- exit runner.exit_value
103
- end
79
+ runner R10K::Action::CriRunner.wrap(R10K::Action::Deploy::Module)
104
80
  end
105
81
  end
106
82
  end
107
- self.command.add_command(Module.command)
108
83
 
109
84
  module Display
110
85
  def self.command
111
86
  @cmd ||= Cri::Command.define do
112
87
  name 'display'
88
+ aliases 'list'
113
89
  usage 'display'
114
90
  summary 'Display environments and modules in the deployment'
115
91
 
116
92
  flag :p, :puppetfile, 'Display Puppetfile modules'
93
+ flag nil, :detail, 'Display detailed information'
117
94
 
118
- run do |opts, args, cmd|
119
- deploy = R10K::Deployment.load_config(opts[:config])
120
-
121
- task = R10K::Task::Deployment::Display.new(deploy)
122
- task.puppetfile = opts[:puppetfile]
123
-
124
- runner = R10K::TaskRunner.new(:trace => opts[:trace])
125
- runner.prepend_task task
126
- runner.run
127
-
128
- exit runner.exit_value
129
- end
95
+ runner R10K::Action::CriRunner.wrap(R10K::Action::Deploy::Display)
130
96
  end
131
97
  end
132
98
  end
133
- self.command.add_command(Display.command)
134
99
  end
135
- self.command.add_command(Deploy.command)
136
100
  end
101
+
102
+ R10K::CLI.command.add_command(R10K::CLI::Deploy.command)
103
+ R10K::CLI::Deploy.command.add_command(R10K::CLI::Deploy::Environment.command)
104
+ R10K::CLI::Deploy.command.add_command(R10K::CLI::Deploy::Module.command)
105
+ R10K::CLI::Deploy.command.add_command(R10K::CLI::Deploy::Display.command)
@@ -1,5 +1,6 @@
1
1
  require 'r10k/cli'
2
2
  require 'r10k/puppetfile'
3
+ require 'r10k/action/puppetfile'
3
4
 
4
5
  require 'cri'
5
6
 
@@ -30,54 +31,25 @@ Puppetfile (http://bombasticmonkey.com/librarian-puppet/).
30
31
  usage 'install'
31
32
  summary 'Install all modules from a Puppetfile'
32
33
 
33
- run do |opts, args, cmd|
34
- puppetfile_root = Dir.getwd
35
- puppetfile_path = ENV['PUPPETFILE_DIR']
36
- puppetfile = ENV['PUPPETFILE']
37
-
38
- puppetfile = R10K::Puppetfile.new(puppetfile_root, puppetfile_path, puppetfile)
39
-
40
- runner = R10K::TaskRunner.new(:trace => opts[:trace])
41
- task = R10K::Task::Puppetfile::Sync.new(puppetfile)
42
- runner.append_task task
43
-
44
- runner.run
45
-
46
- exit runner.exit_value
47
- end
34
+ # @todo add --moduledir option
35
+ # @todo add --puppetfile option
36
+ # @todo add --no-purge option
37
+ runner R10K::Action::Puppetfile::CriRunner.wrap(R10K::Action::Puppetfile::Install)
48
38
  end
49
39
  end
50
40
  end
51
- self.command.add_command(Install.command)
41
+
52
42
  module Check
53
43
  def self.command
54
44
  @cmd ||= Cri::Command.define do
55
45
  name 'check'
56
46
  usage 'check'
57
47
  summary 'Try and load the Puppetfile to verify the syntax is correct.'
58
- run do |opts,args,cmd|
59
- puppetfile_root = Dir.getwd
60
- puppetfile_path = ENV['PUPPETFILE_DIR']
61
- puppetfile = ENV['PUPPETFILE']
62
-
63
- puppetfile = R10K::Puppetfile.new(puppetfile_root, puppetfile_path, puppetfile)
64
- begin
65
- puppetfile.load
66
- rescue SyntaxError, LoadError => e
67
- $stderr.puts "ERROR: Could not parse Puppetfile"
68
- $stderr.puts e.message
69
- if opts[:trace]
70
- $stderr.puts e.backtrace.join("\n")
71
- end
72
- exit 1
73
- end
74
- puts "Syntax OK"
75
- exit 0
76
- end
48
+ runner R10K::Action::Puppetfile::CriRunner.wrap(R10K::Action::Puppetfile::Check)
77
49
  end
78
50
  end
79
51
  end
80
- self.command.add_command(Check.command)
52
+
81
53
  module Purge
82
54
  def self.command
83
55
  @cmd ||= Cri::Command.define do
@@ -85,25 +57,15 @@ Puppetfile (http://bombasticmonkey.com/librarian-puppet/).
85
57
  usage 'purge'
86
58
  summary 'Purge unmanaged modules from a Puppetfile managed directory'
87
59
 
88
- run do |opts, args, cmd|
89
- puppetfile_root = Dir.getwd
90
- puppetfile_path = ENV['PUPPETFILE_DIR']
91
- puppetfile = ENV['PUPPETFILE']
92
-
93
- puppetfile = R10K::Puppetfile.new(puppetfile_root, puppetfile_path, puppetfile)
94
-
95
- runner = R10K::TaskRunner.new(:trace => opts[:trace])
96
- task = R10K::Task::Puppetfile::Purge.new(puppetfile)
97
- runner.append_task task
98
-
99
- runner.run
100
-
101
- exit runner.exit_value
102
- end
60
+ runner R10K::Action::Puppetfile::CriRunner.wrap(R10K::Action::Puppetfile::Purge)
103
61
  end
104
62
  end
105
63
  end
106
- self.command.add_command(Purge.command)
107
64
  end
108
- self.command.add_command(Puppetfile.command)
109
65
  end
66
+
67
+ R10K::CLI.command.add_command(R10K::CLI::Puppetfile.command)
68
+
69
+ R10K::CLI::Puppetfile.command.add_command(R10K::CLI::Puppetfile::Install.command)
70
+ R10K::CLI::Puppetfile.command.add_command(R10K::CLI::Puppetfile::Check.command)
71
+ R10K::CLI::Puppetfile.command.add_command(R10K::CLI::Puppetfile::Purge.command)
@@ -1,74 +1,129 @@
1
- require 'yaml'
2
- require 'r10k'
3
1
  require 'r10k/source'
2
+ require 'r10k/util/basedir'
3
+ require 'r10k/errors'
4
+ require 'set'
4
5
 
5
6
  module R10K
6
- class Deployment
7
- # Model a full installation of module directories and modules.
7
+ # A deployment models the entire state of the configuration that a Puppet
8
+ # master can use. It contains a set of sources that can produce environments
9
+ # and manages the contents of directories where environments are deployed.
10
+ #
11
+ # @api private
12
+ class Deployment
8
13
 
9
- require 'r10k/deployment/environment'
10
- require 'r10k/deployment/basedir'
11
- require 'r10k/deployment/source'
12
- require 'r10k/deployment/config'
14
+ require 'r10k/deployment/environment'
15
+ require 'r10k/deployment/basedir'
16
+ require 'r10k/deployment/source'
17
+ require 'r10k/deployment/config'
13
18
 
14
- # Generate a deployment object based on a config
15
- #
16
- # @param path [String] The path to the deployment config
17
- # @return [R10K::Deployment] The deployment loaded with the given config
18
- def self.load_config(path)
19
- config = R10K::Deployment::Config.new(path)
20
- new(config)
21
- end
19
+ # Generate a deployment object based on a config
20
+ #
21
+ # @param path [String] The path to the deployment config
22
+ # @return [R10K::Deployment] The deployment loaded with the given config
23
+ def self.load_config(path)
24
+ config = R10K::Deployment::Config.new(path)
25
+ new(config)
26
+ end
22
27
 
23
- def initialize(config)
24
- @config = config
25
- end
28
+ # @!attribute [r] config
29
+ # @return [R10K::Deployment::Config]
30
+ attr_reader :config
26
31
 
27
- def preload!
28
- sources.each(&:preload!)
29
- end
30
- alias fetch_sources preload!
32
+ def initialize(config)
33
+ @config = config
34
+ end
31
35
 
32
- # Lazily load all sources
33
- #
34
- # This instantiates the @_sources instance variable, but should not be
35
- # used directly as it could be legitimately unset if we're doing lazy
36
- # loading.
37
- #
38
- # @return [Array<R10K::Source::Base>] All repository sources
39
- # specified in the config
40
- def sources
41
- load_sources if @_sources.nil?
42
- @_sources
43
- end
36
+ def preload!
37
+ sources.each(&:preload!)
38
+ end
44
39
 
45
- # Lazily load all environments
46
- #
47
- # This instantiates the @_environments instance variable, but should not be
48
- # used directly as it could be legitimately unset if we're doing lazy
49
- # loading.
50
- #
51
- # @return [Array<R10K::Environment::Base>] All enviroments across
52
- # all sources
53
- def environments
54
- load_environments if @_environments.nil?
55
- @_environments
56
- end
40
+ # @deprecated
41
+ alias fetch_sources preload!
42
+
43
+ # Lazily load all sources
44
+ #
45
+ # This instantiates the @_sources instance variable, but should not be
46
+ # used directly as it could be legitimately unset if we're doing lazy
47
+ # loading.
48
+ #
49
+ # @return [Array<R10K::Source::Base>] All repository sources
50
+ # specified in the config
51
+ def sources
52
+ load_sources if @_sources.nil?
53
+ @_sources
54
+ end
57
55
 
58
- private
56
+ # Lazily load all environments
57
+ #
58
+ # This instantiates the @_environments instance variable, but should not be
59
+ # used directly as it could be legitimately unset if we're doing lazy
60
+ # loading.
61
+ #
62
+ # @return [Array<R10K::Environment::Base>] All enviroments across
63
+ # all sources
64
+ def environments
65
+ load_environments if @_environments.nil?
66
+ @_environments
67
+ end
59
68
 
60
- def load_sources
61
- sources = @config.setting(:sources)
62
- @_sources = sources.map do |(name, hash)|
63
- R10K::Source.from_hash(name, hash)
69
+ # @return [Array<String>] The paths used by all contained sources
70
+ def paths
71
+ paths_and_sources.keys
72
+ end
73
+
74
+ # @return [Hash<String, Array<R10K::Source::Base>]
75
+ def paths_and_sources
76
+ pathmap = Hash.new { |h, k| h[k] = [] }
77
+ sources.each { |source| pathmap[source.basedir] << source }
78
+ pathmap
79
+ end
80
+
81
+ # Remove unmanaged content from all source paths
82
+ def purge!
83
+ paths_and_sources.each_pair do |path, sources_at_path|
84
+ R10K::Util::Basedir.new(path, sources_at_path).purge!
85
+ end
64
86
  end
65
- end
66
87
 
67
- def load_environments
68
- @_environments = []
69
- sources.each do |source|
70
- @_environments += source.environments
88
+ def validate!
89
+ hash = {}
90
+ sources.each do |source|
91
+ source.environments.each do |environment|
92
+ if hash.key?(environment.path)
93
+ osource, oenvironment = hash[environment.path]
94
+ msg = ""
95
+ msg << "Environment collision at #{environment.path} between "
96
+ msg << "#{source.name}:#{environment.name} and #{osource.name}:#{oenvironment.name}"
97
+ raise R10K::R10KError, msg
98
+ else
99
+ hash[environment.path] = [source, environment]
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ def accept(visitor)
106
+ visitor.visit(:deployment, self) do
107
+ sources.each do |env|
108
+ env.accept(visitor)
109
+ end
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ def load_sources
116
+ sources = @config.setting(:sources)
117
+ @_sources = sources.map do |(name, hash)|
118
+ R10K::Source.from_hash(name, hash)
119
+ end
120
+ end
121
+
122
+ def load_environments
123
+ @_environments = []
124
+ sources.each do |source|
125
+ @_environments += source.environments
126
+ end
71
127
  end
72
128
  end
73
129
  end
74
- end