r10k 3.3.3 → 3.5.2

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 (67) hide show
  1. checksums.yaml +5 -5
  2. data/.github/pull_request_template.md +1 -0
  3. data/.github/workflows/docker.yml +56 -0
  4. data/.github/workflows/release.yml +36 -0
  5. data/.travis.yml +23 -8
  6. data/CHANGELOG.mkd +57 -4
  7. data/CODEOWNERS +1 -0
  8. data/Gemfile +1 -1
  9. data/README.mkd +4 -3
  10. data/azure-pipelines.yml +2 -1
  11. data/bin/r10k +1 -1
  12. data/doc/dynamic-environments/configuration.mkd +160 -2
  13. data/doc/dynamic-environments/git-environments.mkd +1 -1
  14. data/doc/dynamic-environments/master-configuration.mkd +28 -58
  15. data/doc/faq.mkd +6 -1
  16. data/doc/puppetfile.mkd +2 -0
  17. data/docker/Makefile +39 -17
  18. data/docker/r10k/Dockerfile +36 -15
  19. data/docker/r10k/adduser.sh +13 -0
  20. data/docker/r10k/docker-entrypoint.d/10-analytics.sh +1 -1
  21. data/docker/r10k/release.Dockerfile +54 -0
  22. data/docker/spec/dockerfile_spec.rb +4 -3
  23. data/docker/spec/fixtures/Puppetfile +1 -1
  24. data/integration/Rakefile +2 -2
  25. data/lib/r10k/action/deploy/environment.rb +7 -3
  26. data/lib/r10k/action/deploy/module.rb +5 -1
  27. data/lib/r10k/action/runner.rb +4 -4
  28. data/lib/r10k/cli/deploy.rb +1 -1
  29. data/lib/r10k/environment.rb +30 -0
  30. data/lib/r10k/environment/bare.rb +16 -0
  31. data/lib/r10k/environment/git.rb +6 -5
  32. data/lib/r10k/environment/svn.rb +2 -0
  33. data/lib/r10k/environment/with_modules.rb +139 -0
  34. data/lib/r10k/forge/module_release.rb +2 -2
  35. data/lib/r10k/logging/terminaloutputter.rb +1 -1
  36. data/lib/r10k/module/base.rb +5 -0
  37. data/lib/r10k/module/forge.rb +5 -1
  38. data/lib/r10k/puppetfile.rb +6 -0
  39. data/lib/r10k/source.rb +4 -0
  40. data/lib/r10k/source/exec.rb +51 -0
  41. data/lib/r10k/source/hash.rb +182 -0
  42. data/lib/r10k/source/yaml.rb +20 -0
  43. data/lib/r10k/source/yamldir.rb +32 -0
  44. data/lib/r10k/util/attempt.rb +1 -1
  45. data/lib/r10k/version.rb +4 -1
  46. data/locales/r10k.pot +65 -22
  47. data/r10k.gemspec +6 -2
  48. data/spec/unit/action/deploy/environment_spec.rb +1 -0
  49. data/spec/unit/action/deploy/module_spec.rb +13 -0
  50. data/spec/unit/action/puppetfile/install_spec.rb +3 -1
  51. data/spec/unit/action/runner_spec.rb +2 -2
  52. data/spec/unit/forge/module_release_spec.rb +14 -10
  53. data/spec/unit/source/exec_spec.rb +81 -0
  54. data/spec/unit/source/hash_spec.rb +54 -0
  55. data/spec/unit/source/yaml_spec.rb +42 -0
  56. metadata +64 -22
  57. data/MAINTAINERS +0 -18
  58. data/docker/distelli-manifest.yml +0 -9
  59. data/integration/scripts/README.mkd +0 -86
  60. data/integration/scripts/setup_r10k_env_centos5.sh +0 -23
  61. data/integration/scripts/setup_r10k_env_centos6.sh +0 -23
  62. data/integration/scripts/setup_r10k_env_rhel7.sh +0 -23
  63. data/integration/scripts/setup_r10k_env_sles11.sh +0 -23
  64. data/integration/scripts/setup_r10k_env_sles12.sh +0 -23
  65. data/integration/scripts/setup_r10k_env_ubuntu1004.sh +0 -23
  66. data/integration/scripts/setup_r10k_env_ubuntu1204.sh +0 -23
  67. data/integration/scripts/setup_r10k_env_ubuntu1404.sh +0 -23
@@ -72,7 +72,11 @@ module R10K
72
72
  end
73
73
 
74
74
  def allowed_initialize_opts
75
- super.merge(environment: true, 'no-force': :self, 'generate-types': :self, 'puppet-path': :self)
75
+ super.merge(environment: true,
76
+ cachedir: :self,
77
+ 'no-force': :self,
78
+ 'generate-types': :self,
79
+ 'puppet-path': :self)
76
80
  end
77
81
  end
78
82
  end
@@ -43,10 +43,10 @@ module R10K
43
43
  config_settings = settings_from_config(@opts[:config])
44
44
 
45
45
  overrides = {}
46
- overrides[:cachedir] = @opts[:cachedir] unless @opts[:cachedir].nil?
47
- overrides[:deploy] = {} if @opts[:'puppet-path'] || @opts[:'generate-types']
48
- overrides[:deploy][:puppet_path] = @opts[:'puppet-path'] unless @opts[:'puppet-path'].nil?
49
- overrides[:deploy][:generate_types] = @opts[:'generate-types'] unless @opts[:'generate-types'].nil?
46
+ overrides[:cachedir] = @opts[:cachedir] if @opts.key?(:cachedir)
47
+ overrides[:deploy] = {} if @opts.key?(:'puppet-path') || @opts.key?(:'generate-types')
48
+ overrides[:deploy][:puppet_path] = @opts[:'puppet-path'] if @opts.key?(:'puppet-path')
49
+ overrides[:deploy][:generate_types] = @opts[:'generate-types'] if @opts.key?(:'generate-types')
50
50
 
51
51
  with_overrides = config_settings.merge(overrides) do |key, oldval, newval|
52
52
  newval = oldval.merge(newval) if oldval.is_a? Hash
@@ -18,7 +18,7 @@ module R10K::CLI
18
18
 
19
19
  description <<-DESCRIPTION
20
20
  `r10k deploy` implements the Git branch to Puppet environment workflow
21
- (https://puppetlabs.com/blog/git-workflow-and-puppet-environments/).
21
+ (https://puppet.com/docs/puppet/latest/environments_about.html).
22
22
  DESCRIPTION
23
23
 
24
24
  required nil, :cachedir, 'Specify a cachedir, overriding the value in config'
@@ -1,6 +1,36 @@
1
1
  module R10K
2
2
  module Environment
3
+ def self.factory
4
+ @factory ||= R10K::KeyedFactory.new
5
+ end
6
+
7
+ def self.register(key, klass)
8
+ factory.register(key, klass)
9
+ end
10
+
11
+ def self.retrieve(key)
12
+ factory.retrieve(key)
13
+ end
14
+
15
+ def self.generate(type, name, basedir, dirname, options = {})
16
+ factory.generate(type, name, basedir, dirname, options)
17
+ end
18
+
19
+ def self.from_hash(name, hash)
20
+ R10K::Util::SymbolizeKeys.symbolize_keys!(hash)
21
+
22
+ basedir = hash.delete(:basedir)
23
+ dirname = hash.delete(:dirname) || name
24
+
25
+ type = hash.delete(:type)
26
+ type = type.is_a?(String) ? type.to_sym : type
27
+
28
+ generate(type, name, basedir, dirname, hash)
29
+ end
30
+
3
31
  require 'r10k/environment/base'
32
+ require 'r10k/environment/with_modules'
33
+ require 'r10k/environment/bare'
4
34
  require 'r10k/environment/git'
5
35
  require 'r10k/environment/svn'
6
36
  end
@@ -0,0 +1,16 @@
1
+ class R10K::Environment::Bare < R10K::Environment::WithModules
2
+
3
+ R10K::Environment.register(:bare, self)
4
+
5
+ def sync
6
+ path.mkpath
7
+ end
8
+
9
+ def status
10
+ :not_applicable
11
+ end
12
+
13
+ def signature
14
+ 'bare-default'
15
+ end
16
+ end
@@ -6,10 +6,14 @@ require 'forwardable'
6
6
  # This class implements an environment based on a Git branch.
7
7
  #
8
8
  # @since 1.3.0
9
- class R10K::Environment::Git < R10K::Environment::Base
9
+ class R10K::Environment::Git < R10K::Environment::WithModules
10
10
 
11
11
  include R10K::Logging
12
12
 
13
+ R10K::Environment.register(:git, self)
14
+ # Register git as the default environment type
15
+ R10K::Environment.register(nil, self)
16
+
13
17
  # @!attribute [r] remote
14
18
  # @return [String] The URL to the remote git repository
15
19
  attr_reader :remote
@@ -66,15 +70,12 @@ class R10K::Environment::Git < R10K::Environment::Base
66
70
 
67
71
  include R10K::Util::Purgeable
68
72
 
69
- def managed_directories
70
- [@full_path]
71
- end
72
-
73
73
  # Returns an array of the full paths to all the content being managed.
74
74
  # @note This implements a required method for the Purgeable mixin
75
75
  # @return [Array<String>]
76
76
  def desired_contents
77
77
  desired = [File.join(@full_path, '.git')]
78
78
  desired += @repo.tracked_paths.map { |entry| File.join(@full_path, entry) }
79
+ desired += super
79
80
  end
80
81
  end
@@ -9,6 +9,8 @@ class R10K::Environment::SVN < R10K::Environment::Base
9
9
 
10
10
  include R10K::Logging
11
11
 
12
+ R10K::Environment.register(:svn, self)
13
+
12
14
  # @!attribute [r] remote
13
15
  # @return [String] The URL to the remote SVN branch to check out
14
16
  attr_reader :remote
@@ -0,0 +1,139 @@
1
+ require 'r10k/logging'
2
+ require 'r10k/util/purgeable'
3
+
4
+ # This abstract base class implements an environment that can include module
5
+ # content
6
+ #
7
+ # @since 3.4.0
8
+ class R10K::Environment::WithModules < R10K::Environment::Base
9
+
10
+ include R10K::Logging
11
+
12
+ # @!attribute [r] moduledir
13
+ # @return [String] The directory to install environment-defined modules
14
+ # into (default: #{basedir}/modules)
15
+ attr_reader :moduledir
16
+
17
+ # Initialize the given environment.
18
+ #
19
+ # @param name [String] The unique name describing this environment.
20
+ # @param basedir [String] The base directory where this environment will be created.
21
+ # @param dirname [String] The directory name for this environment.
22
+ # @param options [Hash] An additional set of options for this environment.
23
+ #
24
+ # @param options [String] :moduledir The path to install modules to
25
+ # @param options [Hash] :modules Modules to add to the environment
26
+ def initialize(name, basedir, dirname, options = {})
27
+ super(name, basedir, dirname, options)
28
+
29
+ @managed_content = {}
30
+ @modules = []
31
+ @moduledir = case options[:moduledir]
32
+ when nil
33
+ File.join(@basedir, @dirname, 'modules')
34
+ when File.absolute_path(options[:moduledir])
35
+ options.delete(:moduledir)
36
+ else
37
+ File.join(@basedir, @dirname, options.delete(:moduledir))
38
+ end
39
+
40
+ modhash = options.delete(:modules)
41
+ load_modules(modhash) unless modhash.nil?
42
+ end
43
+
44
+ # @return [Array<R10K::Module::Base>] All modules associated with this environment.
45
+ # Modules may originate from either:
46
+ # - The r10k environment object
47
+ # - A Puppetfile in the environment's content
48
+ def modules
49
+ return @modules if @puppetfile.nil?
50
+
51
+ @puppetfile.load unless @puppetfile.loaded?
52
+ @modules + @puppetfile.modules
53
+ end
54
+
55
+ def accept(visitor)
56
+ visitor.visit(:environment, self) do
57
+ @modules.each do |mod|
58
+ mod.accept(visitor)
59
+ end
60
+
61
+ puppetfile.accept(visitor)
62
+ validate_no_module_conflicts
63
+ end
64
+ end
65
+
66
+ def load_modules(module_hash)
67
+ module_hash.each do |name, args|
68
+ add_module(name, args)
69
+ end
70
+ end
71
+
72
+ # @param [String] name
73
+ # @param [*Object] args
74
+ def add_module(name, args)
75
+ if args.is_a?(Hash)
76
+ # symbolize keys in the args hash
77
+ args = args.inject({}) { |memo,(k,v)| memo[k.to_sym] = v; memo }
78
+ end
79
+
80
+ if args.is_a?(Hash) && install_path = args.delete(:install_path)
81
+ install_path = resolve_install_path(install_path)
82
+ validate_install_path(install_path, name)
83
+ else
84
+ install_path = @moduledir
85
+ end
86
+
87
+ # Keep track of all the content this environment is managing to enable purging.
88
+ @managed_content[install_path] = Array.new unless @managed_content.has_key?(install_path)
89
+
90
+ mod = R10K::Module.new(name, install_path, args, self.name)
91
+ mod.origin = 'Environment'
92
+
93
+ @managed_content[install_path] << mod.name
94
+ @modules << mod
95
+ end
96
+
97
+ def validate_no_module_conflicts
98
+ @puppetfile.load unless @puppetfile.loaded?
99
+ conflicts = (@modules + @puppetfile.modules)
100
+ .group_by { |mod| mod.name }
101
+ .select { |_, v| v.size > 1 }
102
+ .map(&:first)
103
+ unless conflicts.empty?
104
+ msg = _('Puppetfile cannot contain module names defined by environment %{name}') % {name: self.name}
105
+ msg += ' '
106
+ msg += _("Remove the conflicting definitions of the following modules: %{conflicts}" % { conflicts: conflicts.join(' ') })
107
+ raise R10K::Error.new(msg)
108
+ end
109
+ end
110
+
111
+ include R10K::Util::Purgeable
112
+
113
+ # Returns an array of the full paths that can be purged.
114
+ # @note This implements a required method for the Purgeable mixin
115
+ # @return [Array<String>]
116
+ def managed_directories
117
+ [@full_path]
118
+ end
119
+
120
+ # Returns an array of the full paths of filenames that should exist. Files
121
+ # inside managed_directories that are not listed in desired_contents will
122
+ # be purged.
123
+ # @note This implements a required method for the Purgeable mixin
124
+ # @return [Array<String>]
125
+ def desired_contents
126
+ list = @managed_content.keys
127
+ list += @managed_content.flat_map do |install_path, modnames|
128
+ modnames.collect { |name| File.join(install_path, name) }
129
+ end
130
+ end
131
+
132
+ def purge_exclusions
133
+ super + @managed_content.flat_map do |install_path, modnames|
134
+ modnames.map do |name|
135
+ File.join(install_path, name, '**', '*')
136
+ end
137
+ end
138
+ end
139
+ end
@@ -212,14 +212,14 @@ module R10K
212
212
 
213
213
  # Remove the temporary directory used for unpacking the module.
214
214
  def cleanup_unpack_path
215
- if unpack_path.exist?
215
+ if unpack_path.parent.exist?
216
216
  unpack_path.parent.rmtree
217
217
  end
218
218
  end
219
219
 
220
220
  # Remove the downloaded module release.
221
221
  def cleanup_download_path
222
- if download_path.exist?
222
+ if download_path.parent.exist?
223
223
  download_path.parent.rmtree
224
224
  end
225
225
  end
@@ -1,4 +1,4 @@
1
- require 'colored'
1
+ require 'colored2'
2
2
  require 'r10k/logging'
3
3
  require 'log4r/outputter/iooutputter'
4
4
 
@@ -31,6 +31,10 @@ class R10K::Module::Base
31
31
  # @return [R10K::Environment, nil] The parent environment of the module
32
32
  attr_reader :environment
33
33
 
34
+ # @!attribute [rw] origin
35
+ # @return [String] Where the module was sourced from. E.g., "Puppetfile"
36
+ attr_accessor :origin
37
+
34
38
  # There's been some churn over `author` vs `owner` and `full_name` over
35
39
  # `title`, so in the short run it's easier to support both and deprecate one
36
40
  # later.
@@ -47,6 +51,7 @@ class R10K::Module::Base
47
51
  @owner, @name = parse_title(@title)
48
52
  @path = Pathname.new(File.join(@dirname, @name))
49
53
  @environment = environment
54
+ @origin = 'external' # Expect Puppetfile or R10k::Environment to set this to a specific value
50
55
  end
51
56
 
52
57
  # @deprecated
@@ -75,7 +75,11 @@ class R10K::Module::Forge < R10K::Module::Base
75
75
 
76
76
  # @return [String] The version of the currently installed module
77
77
  def current_version
78
- @metadata ? @metadata.version : nil
78
+ if insync?
79
+ (@metadata ||= @metadata_file.read).nil? ? nil : @metadata.version
80
+ else
81
+ nil
82
+ end
79
83
  end
80
84
 
81
85
  alias version current_version
@@ -64,6 +64,7 @@ class Puppetfile
64
64
  end
65
65
 
66
66
  def load(default_branch_override = nil)
67
+ return true if self.loaded?
67
68
  if File.readable? @puppetfile_path
68
69
  self.load!(default_branch_override)
69
70
  else
@@ -83,6 +84,10 @@ class Puppetfile
83
84
  raise R10K::Error.wrap(e, _("Failed to evaluate %{path}") % {path: @puppetfile_path})
84
85
  end
85
86
 
87
+ def loaded?
88
+ @loaded
89
+ end
90
+
86
91
  # @param [Array<String>] modules
87
92
  def validate_no_duplicate_names(modules)
88
93
  dupes = modules
@@ -129,6 +134,7 @@ class Puppetfile
129
134
  @managed_content[install_path] = Array.new unless @managed_content.has_key?(install_path)
130
135
 
131
136
  mod = R10K::Module.new(name, install_path, args, @environment)
137
+ mod.origin = 'Puppetfile'
132
138
 
133
139
  @managed_content[install_path] << mod.name
134
140
  @modules << mod
@@ -32,7 +32,11 @@ module R10K
32
32
  end
33
33
 
34
34
  require 'r10k/source/base'
35
+ require 'r10k/source/hash'
35
36
  require 'r10k/source/git'
36
37
  require 'r10k/source/svn'
38
+ require 'r10k/source/yaml'
39
+ require 'r10k/source/yamldir'
40
+ require 'r10k/source/exec'
37
41
  end
38
42
  end
@@ -0,0 +1,51 @@
1
+ require 'r10k/util/subprocess'
2
+ require 'json'
3
+ require 'yaml'
4
+
5
+ class R10K::Source::Exec < R10K::Source::Hash
6
+ R10K::Source.register(:exec, self)
7
+
8
+ def initialize(name, basedir, options = {})
9
+ unless @command = options[:command]
10
+ raise ConfigError, _('Environment source %{name} missing required parameter: command') % {name: name}
11
+ end
12
+
13
+ # We haven't set the environments option yet. We will do that by
14
+ # overloading the #environments method.
15
+ super(name, basedir, options)
16
+ end
17
+
18
+ def environments_hash
19
+ @environments_hash ||= set_environments_hash(run_environments_command)
20
+ end
21
+
22
+ private
23
+
24
+ def run_environments_command
25
+ subproc = R10K::Util::Subprocess.new([@command])
26
+ subproc.raise_on_fail = true
27
+ subproc.logger = self.logger
28
+ procresult = subproc.execute
29
+
30
+ begin
31
+ environments = JSON.parse(procresult.stdout)
32
+ rescue JSON::ParserError => json_err
33
+ begin
34
+ environments = YAML.safe_load(procresult.stdout)
35
+ rescue Psych::SyntaxError => yaml_err
36
+ raise R10K::Error, _("Error parsing command output for exec source %{name}:\n" \
37
+ "Not valid JSON: %{j_msg}\n" \
38
+ "Not valid YAML: %{y_msg}\n" \
39
+ "Stdout:\n%{out}") % {name: name, j_msg: json_err.message, y_msg: yaml_err.message, out: procresult.stdout}
40
+ end
41
+ end
42
+
43
+ unless R10K::Source::Hash.valid_environments_hash?(environments)
44
+ raise R10K::Error, _("Environment source %{name} command %{cmd} did not return valid environment data.\n" \
45
+ 'Returned: %{data}') % {name: name, cmd: @command, data: environments}
46
+ end
47
+
48
+ # Return the resulting environments hash
49
+ environments
50
+ end
51
+ end
@@ -0,0 +1,182 @@
1
+ # This class implements an environment source based on recieving a hash of
2
+ # environments
3
+ #
4
+ # @since 3.4.0
5
+ #
6
+ # DESCRIPTION
7
+ #
8
+ # This class implements environments defined by a hash having the following
9
+ # schema:
10
+ #
11
+ # ---
12
+ # type: object
13
+ # additionalProperties:
14
+ # type: object
15
+ # properties:
16
+ # type:
17
+ # type: string
18
+ # basedir:
19
+ # type: string
20
+ # modules:
21
+ # type: object
22
+ # additionalProperties:
23
+ # type: object
24
+ # moduledir:
25
+ # type: string
26
+ # additionalProperties:
27
+ # type: string
28
+ #
29
+ # The top-level keys in the hash are environment names. Keys in individual
30
+ # environments should be the same as those which would be given to define a
31
+ # single source in r10k.yaml. Additionally, the "modules" key (and moduledir)
32
+ # can be used to designate module content for the environment, independent of
33
+ # the base source parameters.
34
+ #
35
+ # Example:
36
+ #
37
+ # ---
38
+ # production:
39
+ # type: git
40
+ # remote: 'https://github.com/reidmv/control-repo.git'
41
+ # ref: '1.0.0'
42
+ # modules:
43
+ # geoffwilliams-r_profile: '1.1.0'
44
+ # geoffwilliams-r_role: '2.0.0'
45
+ #
46
+ # development:
47
+ # type: git
48
+ # remote: 'https://github.com/reidmv/control-repo.git'
49
+ # ref: 'master'
50
+ # modules:
51
+ # geoffwilliams-r_profile: '1.1.0'
52
+ # geoffwilliams-r_role: '2.0.0'
53
+ #
54
+ # USAGE
55
+ #
56
+ # The following is an example implementation class showing how to use the
57
+ # R10K::Source::Hash abstract base class. Assume an r10k.yaml file such as:
58
+ #
59
+ # ---
60
+ # sources:
61
+ # proof-of-concept:
62
+ # type: demo
63
+ # basedir: '/etc/puppetlabs/code/environments'
64
+ #
65
+ # Class implementation:
66
+ #
67
+ # class R10K::Source::Demo < R10K::Source::Hash
68
+ # R10K::Source.register(:demo, self)
69
+ #
70
+ # def initialize(name, basedir, options = {})
71
+ # # This is just a demo class, so we hard-code an example :environments
72
+ # # hash here. In a real class, we might do something here such as
73
+ # # perform an API call to retrieve an :environments hash.
74
+ # options[:environments] = {
75
+ # 'production' => {
76
+ # 'remote' => 'https://git.example.com/puppet/control-repo.git',
77
+ # 'ref' => 'release-141',
78
+ # 'modules' => {
79
+ # 'puppetlabs-stdlib' => '6.1.0',
80
+ # 'puppetlabs-ntp' => '8.1.0',
81
+ # 'example-myapp1' => {
82
+ # 'git' => 'https://git.example.com/puppet/example-myapp1.git',
83
+ # 'ref' => 'v1.3.0',
84
+ # },
85
+ # },
86
+ # },
87
+ # 'development' => {
88
+ # 'remote' => 'https://git.example.com/puppet/control-repo.git',
89
+ # 'ref' => 'master',
90
+ # 'modules' => {
91
+ # 'puppetlabs-stdlib' => '6.1.0',
92
+ # 'puppetlabs-ntp' => '8.1.0',
93
+ # 'example-myapp1' => {
94
+ # 'git' => 'https://git.example.com/puppet/example-myapp1.git',
95
+ # 'ref' => 'v1.3.1',
96
+ # },
97
+ # },
98
+ # },
99
+ # }
100
+ #
101
+ # # All we need to do is supply options with the :environments hash.
102
+ # # The R10K::Source::Hash parent class takes care of the rest.
103
+ # super(name, basedir, options)
104
+ # end
105
+ # end
106
+ #
107
+ # Example output:
108
+ #
109
+ # [root@master:~] % r10k deploy environment production -pv
110
+ # INFO -> Using Puppetfile '/etc/puppetlabs/code/environments/production/Puppetfile'
111
+ # INFO -> Using Puppetfile '/etc/puppetlabs/code/environments/development/Puppetfile'
112
+ # INFO -> Deploying environment /etc/puppetlabs/code/environments/production
113
+ # INFO -> Environment production is now at 74ea2e05bba796918e4ff1803018c526337ef5f3
114
+ # INFO -> Deploying Environment content /etc/puppetlabs/code/environments/production/modules/stdlib
115
+ # INFO -> Deploying Environment content /etc/puppetlabs/code/environments/production/modules/ntp
116
+ # INFO -> Deploying Environment content /etc/puppetlabs/code/environments/production/modules/myapp1
117
+ # INFO -> Deploying Puppetfile content /etc/puppetlabs/code/environments/production/modules/ruby_task_helper
118
+ # INFO -> Deploying Puppetfile content /etc/puppetlabs/code/environments/production/modules/bolt_shim
119
+ # INFO -> Deploying Puppetfile content /etc/puppetlabs/code/environments/production/modules/apply_helpers
120
+ #
121
+ class R10K::Source::Hash < R10K::Source::Base
122
+
123
+ include R10K::Logging
124
+
125
+ # @param hash [Hash] A hash to validate.
126
+ # @return [Boolean] False if the hash is obviously invalid. A true return
127
+ # means _maybe_ it's valid.
128
+ def self.valid_environments_hash?(hash)
129
+ # TODO: more robust schema valiation
130
+ hash.is_a?(Hash)
131
+ end
132
+
133
+ # @param name [String] The identifier for this source.
134
+ # @param basedir [String] The base directory where the generated environments will be created.
135
+ # @param options [Hash] An additional set of options for this source. The
136
+ # semantics of this hash may depend on the source implementation.
137
+ #
138
+ # @option options [Boolean, String] :prefix If a String this becomes the prefix.
139
+ # If true, will use the source name as the prefix. All sources should respect this option.
140
+ # Defaults to false for no environment prefix.
141
+ # @option options [Hash] :environments The hash definition of environments
142
+ def initialize(name, basedir, options = {})
143
+ super(name, basedir, options)
144
+ end
145
+
146
+ # Set the environment hash for the source. The environment hash is what the
147
+ # source uses to generate enviroments.
148
+ # @param hash [Hash] The hash to sanitize and use as the source's environments.
149
+ # Should be formatted for use with R10K::Environment#from_hash.
150
+ def set_environments_hash(hash)
151
+ @environments_hash = hash.reduce({}) do |memo,(name,opts)|
152
+ R10K::Util::SymbolizeKeys.symbolize_keys!(opts)
153
+ memo.merge({
154
+ name => opts.merge({
155
+ :basedir => @basedir,
156
+ :dirname => R10K::Environment::Name.new(name, {prefix: @prefix, source: @name}).dirname
157
+ })
158
+ })
159
+ end
160
+ end
161
+
162
+ # Return the sanitized environments hash for this source. The environments
163
+ # hash should contain objects formatted for use with R10K::Environment#from_hash.
164
+ # If the hash does not exist it will be built based on @options.
165
+ def environments_hash
166
+ @environments_hash ||= set_environments_hash(@options.fetch(:environments, {}))
167
+ end
168
+
169
+ def environments
170
+ @environments ||= environments_hash.map do |name, hash|
171
+ R10K::Environment.from_hash(name, hash)
172
+ end
173
+ end
174
+
175
+ # List all environments that should exist in the basedir for this source
176
+ # @note This is required by {R10K::Util::Basedir}
177
+ # @return [Array<String>]
178
+ def desired_contents
179
+ environments.map {|env| env.dirname }
180
+ end
181
+
182
+ end