Module: Beaker::DSL::InstallUtils::ModuleUtils

Included in:
BeakerPuppet
Defined in:
lib/beaker-puppet/install_utils/module_utils.rb

Overview

This module contains methods to help install puppet modules

To mix this is into a class you need the following:

  • a method hosts that yields any hosts implementing Host‘s interface to act upon.

  • a method options that provides an options hash, see Options::OptionsHash

  • the module Roles that provides access to the various hosts implementing Host‘s interface to act upon

  • the module Wrappers the provides convenience methods for Command creation

Constant Summary collapse

PUPPET_MODULE_INSTALL_IGNORE =

The directories in the module directory that will not be scp-ed to the test system when using ‘copy_module_to`

['/.bundle', '/.git', '/.idea', '/.vagrant', '/.vendor', '/vendor', '/acceptance',
'/bundle', '/spec', '/tests', '/log', '/.svn', '/junit', '/pkg', '/example', '/tmp',]

Instance Method Summary collapse

Instance Method Details

#build_ignore_list(opts = {}) ⇒ Object

Build an array list of files/directories to ignore when pushing to remote host Automatically adds ‘..’ and ‘.’ to array. If not opts of :ignore list is provided it will use the static variable PUPPET_MODULE_INSTALL_IGNORE

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :ignore_list (Array)

    A list of files/directories to ignore



236
237
238
239
240
241
242
243
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 236

def build_ignore_list(opts = {})
  ignore_list = opts[:ignore_list] || PUPPET_MODULE_INSTALL_IGNORE
  raise ArgumentError 'Ignore list must be an Array' if !ignore_list.is_a?(Array) || ignore_list.nil?

  ignore_list << '.' unless ignore_list.include? '.'
  ignore_list << '..' unless ignore_list.include? '..'
  ignore_list
end

#copy_module_to(one_or_more_hosts, opts = {}) ⇒ Object Also known as: copy_root_module_to

Install local module for acceptance testing should be used as a presuite to ensure local module is copied to the hosts you want, particularly masters

Parameters:

  • one_or_more_hosts (Host, Array<Host>, String, Symbol)

    One or more hosts to act upon, or a role (String or Symbol) that identifies one or more hosts.

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :source (String) — default: './'

    The current directory where the module sits, otherwise will try

    and walk the tree to figure out
    
  • :module_name (String) — default: nil

    Name which the module should be installed under, please do not include author,

    if none is provided it will attempt to parse the metadata.json and then the Module file to determine
    the name of the module
    
  • :target_module_path (String) — default: host['distmoduledir']/modules

    Location where the module should be installed, will default

    to host['distmoduledir']/modules
    
  • :ignore_list (Array)
  • :protocol (String)

    Name of the underlying transfer method. Valid options are ‘scp’ or ‘rsync’.

Raises:

  • (ArgumentError)

    if not host is provided or module_name is not provided and can not be found in Modulefile



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 113

def copy_module_to(one_or_more_hosts, opts = {})
  block_on one_or_more_hosts do |host|
    opts = { source: './',
             target_module_path: host['distmoduledir'],
             ignore_list: PUPPET_MODULE_INSTALL_IGNORE, }.merge(opts)

    ignore_list = build_ignore_list(opts)
    target_module_dir = get_target_module_path(host, opts[:target_module_path])
    source_path = File.expand_path(opts[:source])
    source_name = File.basename(source_path)
    if opts.has_key?(:module_name)
      module_name = opts[:module_name]
    else
      _, module_name = parse_for_modulename(source_path)
    end

    target_path = File.join(target_module_dir, module_name)
    target_path = target_path.gsub(%r{/}, '\\') if host.is_powershell? # make sure our slashes are correct

    opts[:protocol] ||= 'scp'
    case opts[:protocol]
    when 'scp'
      # move to the host
      logger.debug "Using scp to transfer #{source_path} to #{target_path}"
      scp_to host, source_path, target_module_dir, { ignore: ignore_list }

      # rename to the selected module name, if not correct
      cur_path = File.join(target_module_dir, source_name)
      cur_path = cur_path.gsub(%r{/}, '\\') if host.is_powershell? # make sure our slashes are correct
      host.mv cur_path, target_path unless cur_path == target_path
    when 'rsync'
      logger.debug "Using rsync to transfer #{source_path} to #{target_path}"
      rsync_to host, source_path, target_path, { ignore: ignore_list }
    else
      logger.debug 'Unsupported transfer protocol, returning nil'
      nil
    end
  end
end

#get_module_name(author_module_name) ⇒ String?

Parse modulename from the pattern ‘Auther-ModuleName’

Parameters:

  • author_module_name (String)

    <Author>-<ModuleName> pattern

Returns:

  • (String, nil)


211
212
213
214
215
216
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 211

def get_module_name(author_module_name)
  split_name = split_author_modulename(author_module_name)
  return unless split_name

  [split_name[:author], split_name[:module]]
end

#get_target_module_path(host, path = nil) ⇒ Object



154
155
156
157
158
159
160
161
162
163
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 154

def get_target_module_path(host, path = nil)
  if path
    on(host, "echo #{path}").stdout.chomp
  else
    path = host.puppet['basemodulepath'].split(':').first
    raise ArgumentError, 'Unable to find target module path to copy to' unless path

    path
  end
end

#install_dev_puppet_module(opts) ⇒ Object Also known as: puppet_module_install

Install the desired module on all hosts using either the PMT or a

staging forge

Passes options through to either ‘install_puppet_module_via_pmt_on`

or `copy_module_to`

Examples:

Installing a module from the local directory

install_dev_puppet_module( :source => './', :module_name => 'concat' )

Installing a module from a staging forge

options[:forge_host] = 'my-forge-api.example.com'
install_dev_puppet_module( :source => './', :module_name => 'concat' )

Parameters:

  • opts (Hash)

See Also:



50
51
52
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 50

def install_dev_puppet_module(opts)
  block_on(hosts) { |h| install_dev_puppet_module_on(h, opts) }
end

#install_dev_puppet_module_on(host, opts) ⇒ Object Also known as: puppet_module_install_on

Install the desired module on all hosts using either the PMT or a

staging forge


24
25
26
27
28
29
30
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 24

def install_dev_puppet_module_on(host, opts)
  if options[:forge_host]
    install_puppet_module_via_pmt_on(host, opts)
  else
    copy_module_to(host, opts)
  end
end

#install_puppet_module_via_pmt(opts = {}) ⇒ Object

Install the desired module with the PMT on all known hosts



89
90
91
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 89

def install_puppet_module_via_pmt(opts = {})
  install_puppet_module_via_pmt_on(hosts, opts)
end

#install_puppet_module_via_pmt_on(host, opts = {}) ⇒ Object

Install the desired module with the PMT on a given host

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :module_name (String)

    The short name of the module to be installed

  • :version (String)

    The version of the module to be installed



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 60

def install_puppet_module_via_pmt_on(host, opts = {})
  block_on host do |h|
    version_info = opts[:version] ? "-v #{opts[:version]}" : ''
    if opts[:source]
      author_name, module_name = parse_for_modulename(opts[:source])
      modname = "#{author_name}-#{module_name}"
    else
      modname = opts[:module_name]
    end

    puppet_opts = {}
    if host[:default_module_install_opts].respond_to? :merge
      puppet_opts = host[:default_module_install_opts].merge(puppet_opts)
    end

    if options[:forge_host]
      puppet_opts[:module_repository] = if options[:forge_host] =~ /^http/
                                          options[:forge_host]
                                        else
                                          "https://#{options[:forge_host]}"
                                        end
    end

    on h, puppet("module install #{modname} #{version_info}", puppet_opts)
  end
end

#parse_for_modulename(root_module_dir) ⇒ String

Parse root directory of a module for module name Searches for metadata.json and then if none found, Modulefile and parses for the Name attribute

Parameters:

  • root_module_dir (String)

Returns:

  • (String)

    module name



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 187

def parse_for_modulename(root_module_dir)
  author_name = nil
  module_name = nil
  if File.exist?("#{root_module_dir}/metadata.json")
    logger.debug 'Attempting to parse Modulename from metadata.json'
    module_json = JSON.parse(File.read("#{root_module_dir}/metadata.json"))
    author_name, module_name = get_module_name(module_json['name']) if module_json.has_key?('name')
  end
  if !module_name && File.exist?("#{root_module_dir}/Modulefile")
    logger.debug 'Attempting to parse Modulename from Modulefile'
    if /^name\s+'?(\w+-\w+)'?\s*$/i.match(File.read("#{root_module_dir}/Modulefile"))
      author_name, module_name = get_module_name(Regexp.last_match[1])
    end
  end
  logger.debug 'Unable to determine name, returning null' if !module_name && !author_name
  [author_name, module_name]
end

#parse_for_moduleroot(possible_module_directory) ⇒ String?

Recursive method for finding the module root Assumes that a Modulefile exists

Parameters:

  • possible_module_directory (String)

    will look for Modulefile and if none found go up one level and try again until root is reached

Returns:

  • (String, nil)


171
172
173
174
175
176
177
178
179
180
181
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 171

def parse_for_moduleroot(possible_module_directory)
  if File.exist?("#{possible_module_directory}/Modulefile") || File.exist?("#{possible_module_directory}/metadata.json")
    possible_module_directory
  elsif possible_module_directory === '/'
    logger.error "At root, can't parse for another directory"
    nil
  else
    logger.debug "No Modulefile or metadata.json found at #{possible_module_directory}, moving up"
    parse_for_moduleroot File.expand_path(File.join(possible_module_directory, '..'))
  end
end

#split_author_modulename(author_module_attr) ⇒ Hash<Symbol,String>?

Split the Author-Name into a hash

Parameters:

  • author_module_attr (String)

Returns:

  • (Hash<Symbol,String>, nil)

    :author and :module symbols will be returned



223
224
225
226
227
228
# File 'lib/beaker-puppet/install_utils/module_utils.rb', line 223

def split_author_modulename(author_module_attr)
  result = /(\w+)-(\w+)/.match(author_module_attr)
  return unless result

  { author: result[1], module: result[2] }
end