r10k 3.3.3 → 3.4.0

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.
@@ -8,9 +8,11 @@ describe 'r10k container' do
8
8
  include Pupperware::SpecHelpers
9
9
  def run_r10k(command)
10
10
  run_command("docker run --detach \
11
- --volume #{File.join(SPEC_DIRECTORY, 'fixtures')}:/test \
11
+ --volume #{File.join(SPEC_DIRECTORY, 'fixtures')}:/home/puppet/test \
12
12
  #{@image} #{command} \
13
- --puppetfile /test/Puppetfile")
13
+ --verbose \
14
+ --trace \
15
+ --puppetfile test/Puppetfile")
14
16
  end
15
17
 
16
18
  before(:all) do
@@ -35,7 +37,6 @@ describe 'r10k container' do
35
37
  container = result[:stdout].chomp
36
38
  wait_on_container_exit(container)
37
39
  expect(get_container_exit_code(container)).to eq(0)
38
- expect(Dir.exist?(File.join(SPEC_DIRECTORY, 'fixtures', 'modules', 'ntp'))).to eq(true)
39
40
  emit_log(container)
40
41
  teardown_container(container)
41
42
  end
@@ -1,2 +1,2 @@
1
- moduledir 'test/modules'
1
+ moduledir '/tmp/modules'
2
2
  mod 'puppetlabs/ntp'
@@ -140,14 +140,14 @@ module R10K
140
140
  end
141
141
 
142
142
  def visit_module(mod)
143
- logger.info _("Deploying Puppetfile content %{mod_path}") % {mod_path: mod.path}
143
+ logger.info _("Deploying %{origin} content %{path}") % {origin: mod.origin, path: mod.path}
144
144
  mod.sync(force: @force)
145
145
  end
146
146
 
147
147
  def write_environment_info!(environment, started_at, success)
148
148
  module_deploys = []
149
149
  begin
150
- environment.puppetfile.modules.each do |mod|
150
+ environment.modules.each do |mod|
151
151
  name = mod.name
152
152
  version = mod.version
153
153
  sha = mod.repo.head rescue nil
@@ -44,7 +44,7 @@ module R10K
44
44
 
45
45
  overrides = {}
46
46
  overrides[:cachedir] = @opts[:cachedir] unless @opts[:cachedir].nil?
47
- overrides[:deploy] = {} if @opts[:'puppet-path'] || @opts[:'generate-types']
47
+ overrides[:deploy] = {} if !@opts[:'puppet-path'].nil? || !@opts[:'generate-types'].nil?
48
48
  overrides[:deploy][:puppet_path] = @opts[:'puppet-path'] unless @opts[:'puppet-path'].nil?
49
49
  overrides[:deploy][:generate_types] = @opts[:'generate-types'] unless @opts[:'generate-types'].nil?
50
50
 
@@ -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
@@ -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,10 @@ 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'
37
40
  end
38
41
  end
@@ -0,0 +1,158 @@
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
+ # @param name [String] The identifier for this source.
124
+ # @param basedir [String] The base directory where the generated environments will be created.
125
+ # @param options [Hash] An additional set of options for this source. The
126
+ # semantics of this hash may depend on the source implementation.
127
+ #
128
+ # @option options [Boolean, String] :prefix If a String this becomes the prefix.
129
+ # If true, will use the source name as the prefix. All sources should respect this option.
130
+ # Defaults to false for no environment prefix.
131
+ # @option options [Hash] :environments The hash definition of environments
132
+ def initialize(name, basedir, options = {})
133
+ super(name, basedir, options)
134
+
135
+ @environments_hash = options.delete(:environments) || {}
136
+
137
+ @environments_hash.keys.each do |name|
138
+ # TODO: deal with names that aren't valid
139
+ ::R10K::Util::SymbolizeKeys.symbolize_keys!(@environments_hash[name])
140
+ @environments_hash[name][:basedir] = basedir
141
+ @environments_hash[name][:dirname] = name
142
+ end
143
+ end
144
+
145
+ def environments
146
+ @environments ||= @environments_hash.map do |name, hash|
147
+ R10K::Environment.from_hash(name, hash)
148
+ end
149
+ end
150
+
151
+ # List all environments that should exist in the basedir for this source
152
+ # @note This is required by {R10K::Util::Basedir}
153
+ # @return [Array<String>]
154
+ def desired_contents
155
+ environments.map {|env| env.dirname }
156
+ end
157
+
158
+ end