|
| 1 | +# Main module |
| 2 | +module PuppetlabsSpec; end |
| 3 | +# A Metadata JSON releated functions |
| 4 | +module PuppetlabsSpec::Metadata |
| 5 | + # This method returns an array of dependencies from the metadata.json file |
| 6 | + # in the format of an array of hashes, containing 'remote' (module_name) and |
| 7 | + # optionally 'ref' (version) elements. If no dependencies are specified, |
| 8 | + # empty array is returned |
| 9 | + def module_dependencies_from_metadata(metadata_opts) |
| 10 | + metadata = module_metadata |
| 11 | + return [] unless metadata.key?('dependencies') |
| 12 | + |
| 13 | + opts = metadata_opts['opts'] |
| 14 | + forge = if !opts.nil? && !opts['forge'].nil? |
| 15 | + opts['forge'] |
| 16 | + else |
| 17 | + 'https://forge.puppet.com/' |
| 18 | + end |
| 19 | + dependencies = [] |
| 20 | + metadata['dependencies'].each do |dep| |
| 21 | + tmp = { 'remote' => dep['name'].sub('/', '-') } |
| 22 | + |
| 23 | + if dep.key?('version_requirement') |
| 24 | + tmp['ref'] = module_version_from_requirement( |
| 25 | + tmp['remote'], dep['version_requirement'], forge |
| 26 | + ) |
| 27 | + end |
| 28 | + dependencies.push(tmp) |
| 29 | + end |
| 30 | + |
| 31 | + dependencies |
| 32 | + end |
| 33 | + |
| 34 | + # This method uses the module_source_directory path to read the metadata.json |
| 35 | + # file into a json array |
| 36 | + def module_metadata |
| 37 | + metadata_path = "#{module_source_dir}/metadata.json" |
| 38 | + unless File.exist?(metadata_path) |
| 39 | + raise "Error loading metadata.json file from #{module_source_dir}" |
| 40 | + end |
| 41 | + JSON.parse(File.read(metadata_path)) |
| 42 | + end |
| 43 | + |
| 44 | + private |
| 45 | + |
| 46 | + # This method takes a module name and the version requirement string from the |
| 47 | + # metadata.json file, containing either lower bounds of version or both lower |
| 48 | + # and upper bounds. The function then uses the forge rest endpoint to find |
| 49 | + # the most recent release of the given module matching the version requirement |
| 50 | + def module_version_from_requirement(mod_name, vr_str, forge_api) |
| 51 | + require 'net/http' |
| 52 | + forge_api = File.join(forge_api, '') |
| 53 | + uri = URI.parse("#{forge_api}v3/modules/#{mod_name}") |
| 54 | + req = Net::HTTP::Get.new(uri.request_uri) |
| 55 | + http = Net::HTTP.new(uri.host, uri.port) |
| 56 | + http.use_ssl = (uri.scheme == 'https') |
| 57 | + response = http.request(req) |
| 58 | + forge_data = JSON.parse(response.body) |
| 59 | + |
| 60 | + vrs = version_requirements_from_string(vr_str) |
| 61 | + |
| 62 | + # Here we iterate the releases of the given module and pick the most recent |
| 63 | + # that matches to version requirement |
| 64 | + forge_data['releases'].each do |rel| |
| 65 | + return rel['version'] if vrs.all? { |vr| vr.match?('', rel['version']) } |
| 66 | + end |
| 67 | + |
| 68 | + raise "No release version found matching '#{vr_str}'" |
| 69 | + end |
| 70 | + |
| 71 | + # This method takes a version requirement string as specified in the link |
| 72 | + # below, with either simply a lower bound, or both lower and upper bounds and |
| 73 | + # returns an array of Gem::Dependency objects |
| 74 | + # https://docs.puppet.com/puppet/latest/modules_metadata.html |
| 75 | + def version_requirements_from_string(vr_str) |
| 76 | + ops = vr_str.scan(%r{[(<|>|=)]{1,2}}i) |
| 77 | + vers = vr_str.scan(%r{[(0-9|\.)]+}i) |
| 78 | + |
| 79 | + raise 'Invalid version requirements' if ops.count != 0 && |
| 80 | + ops.count != vers.count |
| 81 | + |
| 82 | + vrs = [] |
| 83 | + ops.each_with_index do |op, index| |
| 84 | + vrs.push(Gem::Dependency.new('', "#{op} #{vers[index]}")) |
| 85 | + end |
| 86 | + |
| 87 | + vrs |
| 88 | + end |
| 89 | + |
| 90 | + # This is a helper for the self-symlink entry of fixtures.yml |
| 91 | + def module_source_dir |
| 92 | + Dir.pwd |
| 93 | + end |
| 94 | +end |
0 commit comments