librarian-puppet-rethinc 3.0.1.1
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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.md +331 -0
- data/bin/librarian-puppet +7 -0
- data/lib/librarian/puppet/action/install.rb +26 -0
- data/lib/librarian/puppet/action/resolve.rb +26 -0
- data/lib/librarian/puppet/action.rb +2 -0
- data/lib/librarian/puppet/cli.rb +117 -0
- data/lib/librarian/puppet/dependency.rb +26 -0
- data/lib/librarian/puppet/dsl.rb +123 -0
- data/lib/librarian/puppet/environment.rb +66 -0
- data/lib/librarian/puppet/extension.rb +9 -0
- data/lib/librarian/puppet/lockfile.rb +78 -0
- data/lib/librarian/puppet/resolver.rb +21 -0
- data/lib/librarian/puppet/rethinc.rb +1 -0
- data/lib/librarian/puppet/source/forge/repo.rb +158 -0
- data/lib/librarian/puppet/source/forge/repo_v1.rb +92 -0
- data/lib/librarian/puppet/source/forge/repo_v3.rb +100 -0
- data/lib/librarian/puppet/source/forge.rb +180 -0
- data/lib/librarian/puppet/source/git.rb +74 -0
- data/lib/librarian/puppet/source/githubtarball/repo.rb +172 -0
- data/lib/librarian/puppet/source/githubtarball.rb +137 -0
- data/lib/librarian/puppet/source/local.rb +176 -0
- data/lib/librarian/puppet/source/path.rb +12 -0
- data/lib/librarian/puppet/source/repo.rb +46 -0
- data/lib/librarian/puppet/source.rb +4 -0
- data/lib/librarian/puppet/templates/Puppetfile +25 -0
- data/lib/librarian/puppet/util.rb +78 -0
- data/lib/librarian/puppet/version.rb +5 -0
- data/lib/librarian/puppet.rb +25 -0
- metadata +242 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'librarian/dsl'
|
2
|
+
require 'librarian/dsl/target'
|
3
|
+
require 'librarian/puppet/source'
|
4
|
+
require 'librarian/puppet/dependency'
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
module Puppet
|
8
|
+
class Dsl < Librarian::Dsl
|
9
|
+
|
10
|
+
FORGE_URL = "https://forgeapi.puppetlabs.com"
|
11
|
+
|
12
|
+
dependency :mod
|
13
|
+
|
14
|
+
source :forge => Source::Forge
|
15
|
+
source :git => Source::Git
|
16
|
+
source :path => Source::Path
|
17
|
+
source :github_tarball => Source::GitHubTarball
|
18
|
+
|
19
|
+
def default_specfile
|
20
|
+
Proc.new do
|
21
|
+
forge FORGE_URL
|
22
|
+
metadata
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.dependency_type
|
27
|
+
Librarian::Puppet::Dependency
|
28
|
+
end
|
29
|
+
|
30
|
+
def post_process_target(target)
|
31
|
+
# save the default forge defined
|
32
|
+
default_forge = target.sources.select {|s| s.is_a? Librarian::Puppet::Source::Forge}.first
|
33
|
+
Librarian::Puppet::Source::Forge.default = default_forge || Librarian::Puppet::Source::Forge.from_lock_options(environment, :remote => FORGE_URL)
|
34
|
+
end
|
35
|
+
|
36
|
+
def receiver(target)
|
37
|
+
Receiver.new(target)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(specfile = nil, sources = [])
|
41
|
+
specfile, sources = nil, specfile if specfile.kind_of?(Array) && sources.empty?
|
42
|
+
|
43
|
+
Target.new(self).tap do |target|
|
44
|
+
target.precache_sources(sources)
|
45
|
+
debug_named_source_cache("Pre-Cached Sources", target)
|
46
|
+
|
47
|
+
specfile ||= Proc.new if block_given?
|
48
|
+
|
49
|
+
if specfile.kind_of?(Pathname) and !File.exists?(specfile)
|
50
|
+
debug { "Specfile #{specfile} not found, using defaults" } unless specfile.nil?
|
51
|
+
receiver(target).run(specfile, &default_specfile)
|
52
|
+
else
|
53
|
+
receiver(target).run(specfile)
|
54
|
+
end
|
55
|
+
|
56
|
+
post_process_target(target)
|
57
|
+
|
58
|
+
debug_named_source_cache("Post-Cached Sources", target)
|
59
|
+
end.to_spec
|
60
|
+
end
|
61
|
+
|
62
|
+
class Target < Librarian::Dsl::Target
|
63
|
+
def dependency(name, *args)
|
64
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
65
|
+
source = source_from_options(options) || @source
|
66
|
+
dep = dependency_type.new(name, args, source, 'Puppetfile')
|
67
|
+
@dependencies << dep
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Receiver < Librarian::Dsl::Receiver
|
72
|
+
attr_reader :specfile, :working_path
|
73
|
+
|
74
|
+
# save the specfile and call librarian
|
75
|
+
def run(specfile = nil)
|
76
|
+
@working_path = specfile.kind_of?(Pathname) ? specfile.parent : Pathname.new(Dir.pwd)
|
77
|
+
@specfile = specfile
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
# implement the 'modulefile' syntax for Puppetfile
|
82
|
+
def modulefile
|
83
|
+
f = modulefile_path
|
84
|
+
raise Error, "Modulefile file does not exist: #{f}" unless File.exists?(f)
|
85
|
+
File.read(f).lines.each do |line|
|
86
|
+
regexp = /\s*dependency\s+('|")([^'"]+)\1\s*(?:,\s*('|")([^'"]+)\3)?/
|
87
|
+
regexp =~ line && mod($2, $4)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# implement the 'metadata' syntax for Puppetfile
|
92
|
+
def metadata
|
93
|
+
f = working_path.join('metadata.json')
|
94
|
+
unless File.exists?(f)
|
95
|
+
msg = "Metadata file does not exist: #{f}"
|
96
|
+
# try modulefile, in case we don't have a Puppetfile and we are using the default template
|
97
|
+
if File.exists?(modulefile_path)
|
98
|
+
modulefile
|
99
|
+
return
|
100
|
+
else
|
101
|
+
raise Error, msg
|
102
|
+
end
|
103
|
+
end
|
104
|
+
begin
|
105
|
+
json = JSON.parse(File.read(f))
|
106
|
+
rescue JSON::ParserError => e
|
107
|
+
raise Error, "Unable to parse json file #{f}: #{e}"
|
108
|
+
end
|
109
|
+
dependencyList = json['dependencies']
|
110
|
+
dependencyList.each do |d|
|
111
|
+
mod(d['name'], d['version_requirement'])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def modulefile_path
|
118
|
+
working_path.join('Modulefile')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "librarian/environment"
|
2
|
+
require "librarian/puppet/dsl"
|
3
|
+
require "librarian/puppet/source"
|
4
|
+
require "librarian/puppet/lockfile"
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
module Puppet
|
8
|
+
class Environment < Librarian::Environment
|
9
|
+
|
10
|
+
def adapter_name
|
11
|
+
"puppet"
|
12
|
+
end
|
13
|
+
|
14
|
+
def lockfile
|
15
|
+
Lockfile.new(self, lockfile_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ephemeral_lockfile
|
19
|
+
Lockfile.new(self, nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
def tmp_path
|
23
|
+
part = config_db["tmp"] || ".tmp"
|
24
|
+
project_path.join(part)
|
25
|
+
end
|
26
|
+
|
27
|
+
def install_path
|
28
|
+
part = config_db["path"] || "modules"
|
29
|
+
project_path.join(part)
|
30
|
+
end
|
31
|
+
|
32
|
+
def vendor_path
|
33
|
+
project_path.join('vendor/puppet')
|
34
|
+
end
|
35
|
+
|
36
|
+
def vendor_cache
|
37
|
+
vendor_path.join('cache')
|
38
|
+
end
|
39
|
+
|
40
|
+
def vendor_source
|
41
|
+
vendor_path.join('source')
|
42
|
+
end
|
43
|
+
|
44
|
+
def vendor!
|
45
|
+
vendor_cache.mkpath unless vendor_cache.exist?
|
46
|
+
vendor_source.mkpath unless vendor_source.exist?
|
47
|
+
end
|
48
|
+
|
49
|
+
def vendor?
|
50
|
+
vendor_path.exist?
|
51
|
+
end
|
52
|
+
|
53
|
+
def local?
|
54
|
+
config_db['mode'] == 'local'
|
55
|
+
end
|
56
|
+
|
57
|
+
def use_v1_api
|
58
|
+
config_db['use-v1-api']
|
59
|
+
end
|
60
|
+
|
61
|
+
def use_short_cache_path
|
62
|
+
config_db['use-short-cache-path']
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Extend Lockfile to normalize module names from acme/mod to acme-mod
|
2
|
+
module Librarian
|
3
|
+
module Puppet
|
4
|
+
class Lockfile < Librarian::Lockfile
|
5
|
+
|
6
|
+
# Extend the parser to normalize module names in old .lock files, converting / to -
|
7
|
+
class Parser < Librarian::Lockfile::Parser
|
8
|
+
|
9
|
+
include Librarian::Puppet::Util
|
10
|
+
|
11
|
+
def extract_and_parse_sources(lines)
|
12
|
+
sources = super
|
13
|
+
sources.each do |source|
|
14
|
+
source[:manifests] = Hash[source[:manifests].map do |name,manifest|
|
15
|
+
[normalize_name(name), manifest]
|
16
|
+
end]
|
17
|
+
end
|
18
|
+
sources
|
19
|
+
end
|
20
|
+
|
21
|
+
def extract_and_parse_dependencies(lines, manifests_index)
|
22
|
+
# when looking up in manifests_index normalize the name beforehand
|
23
|
+
class << manifests_index
|
24
|
+
include Librarian::Puppet::Util
|
25
|
+
alias_method :old_lookup, :[]
|
26
|
+
define_method(:[]) { |k| self.old_lookup(normalize_name(k)) }
|
27
|
+
end
|
28
|
+
dependencies = []
|
29
|
+
while lines.first =~ /^ {2}([\w\-\/]+)(?: \((.*)\))?$/
|
30
|
+
lines.shift
|
31
|
+
name, requirement = $1, $2.split(/,\s*/)
|
32
|
+
dependencies << environment.dsl_class.dependency_type.new(name, requirement, manifests_index[name].source, 'lockfile')
|
33
|
+
end
|
34
|
+
dependencies
|
35
|
+
end
|
36
|
+
|
37
|
+
def compile_placeholder_manifests(sources_ast)
|
38
|
+
manifests = {}
|
39
|
+
sources_ast.each do |source_ast|
|
40
|
+
source_type = source_ast[:type]
|
41
|
+
source = source_type.from_lock_options(environment, source_ast[:options])
|
42
|
+
source_ast[:manifests].each do |manifest_name, manifest_ast|
|
43
|
+
manifests[manifest_name] = ManifestPlaceholder.new(
|
44
|
+
source,
|
45
|
+
manifest_name,
|
46
|
+
manifest_ast[:version],
|
47
|
+
manifest_ast[:dependencies].map do |k, v|
|
48
|
+
environment.dsl_class.dependency_type.new(k, v, nil, manifest_name)
|
49
|
+
end
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
manifests
|
54
|
+
end
|
55
|
+
|
56
|
+
def compile(sources_ast)
|
57
|
+
manifests = compile_placeholder_manifests(sources_ast)
|
58
|
+
manifests = manifests.map do |name, manifest|
|
59
|
+
dependencies = manifest.dependencies.map do |d|
|
60
|
+
environment.dsl_class.dependency_type.new(d.name, d.requirement, manifests[d.name].source, name)
|
61
|
+
end
|
62
|
+
real = Manifest.new(manifest.source, manifest.name)
|
63
|
+
real.version = manifest.version
|
64
|
+
real.dependencies = manifest.dependencies
|
65
|
+
real
|
66
|
+
end
|
67
|
+
ManifestSet.sort(manifests)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def load(string)
|
73
|
+
Parser.new(environment).parse(string)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'librarian/resolver'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
module Puppet
|
5
|
+
class Resolver < Librarian::Resolver
|
6
|
+
|
7
|
+
class Implementation < Librarian::Resolver::Implementation
|
8
|
+
def sourced_dependency_for(dependency)
|
9
|
+
return dependency if dependency.source
|
10
|
+
|
11
|
+
source = dependency_source_map[dependency.name] || default_source
|
12
|
+
dependency.class.new(dependency.name, dependency.requirement, source, dependency.parent)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def implementation(spec)
|
17
|
+
Implementation.new(self, spec, :cyclic => cyclic)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'librarian/puppet'
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'librarian/puppet/util'
|
4
|
+
require 'librarian/puppet/source/repo'
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
module Puppet
|
8
|
+
module Source
|
9
|
+
class Forge
|
10
|
+
class Repo < Librarian::Puppet::Source::Repo
|
11
|
+
include Librarian::Puppet::Util
|
12
|
+
|
13
|
+
def versions
|
14
|
+
return @versions if @versions
|
15
|
+
@versions = get_versions
|
16
|
+
if @versions.empty?
|
17
|
+
info { "No versions found for module #{name}" }
|
18
|
+
else
|
19
|
+
debug { " Module #{name} found versions: #{@versions.join(", ")}" }
|
20
|
+
end
|
21
|
+
@versions
|
22
|
+
end
|
23
|
+
|
24
|
+
# fetch list of versions ordered for newer to older
|
25
|
+
def get_versions
|
26
|
+
# implement in subclasses
|
27
|
+
end
|
28
|
+
|
29
|
+
# return map with dependencies in the form {module_name => version,...}
|
30
|
+
# version: Librarian::Manifest::Version
|
31
|
+
def dependencies(version)
|
32
|
+
# implement in subclasses
|
33
|
+
end
|
34
|
+
|
35
|
+
# return the url for a specific version tarball
|
36
|
+
# version: Librarian::Manifest::Version
|
37
|
+
def url(name, version)
|
38
|
+
# implement in subclasses
|
39
|
+
end
|
40
|
+
|
41
|
+
def manifests
|
42
|
+
versions.map do |version|
|
43
|
+
Manifest.new(source, name, version)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def install_version!(version, install_path)
|
48
|
+
if environment.local? && !vendored?(name, version)
|
49
|
+
raise Error, "Could not find a local copy of #{name} at #{version}."
|
50
|
+
end
|
51
|
+
|
52
|
+
if environment.vendor?
|
53
|
+
vendor_cache(name, version) unless vendored?(name, version)
|
54
|
+
end
|
55
|
+
|
56
|
+
cache_version_unpacked! version
|
57
|
+
|
58
|
+
if install_path.exist? && rsync? != true
|
59
|
+
install_path.rmtree
|
60
|
+
end
|
61
|
+
|
62
|
+
unpacked_path = version_unpacked_cache_path(version).join(module_name(name))
|
63
|
+
|
64
|
+
unless unpacked_path.exist?
|
65
|
+
raise Error, "#{unpacked_path} does not exist, something went wrong. Try removing it manually"
|
66
|
+
else
|
67
|
+
cp_r(unpacked_path, install_path)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def cache_version_unpacked!(version)
|
73
|
+
path = version_unpacked_cache_path(version)
|
74
|
+
return if path.directory?
|
75
|
+
|
76
|
+
# The puppet module command is only available from puppet versions >= 2.7.13
|
77
|
+
#
|
78
|
+
# Specifying the version in the gemspec would force people to upgrade puppet while it's still usable for git
|
79
|
+
# So we do some more clever checking
|
80
|
+
#
|
81
|
+
# Executing older versions or via puppet-module tool gives an exit status = 0 .
|
82
|
+
#
|
83
|
+
check_puppet_module_options
|
84
|
+
|
85
|
+
path.mkpath
|
86
|
+
|
87
|
+
target = vendored?(name, version) ? vendored_path(name, version).to_s : name
|
88
|
+
|
89
|
+
# can't pass the default v3 forge url (http://forgeapi.puppetlabs.com)
|
90
|
+
# to clients that use the v1 API (https://forge.puppet.com)
|
91
|
+
# nor the other way around
|
92
|
+
module_repository = source.uri.to_s
|
93
|
+
|
94
|
+
if Forge.client_api_version() > 1 and module_repository =~ %r{^http(s)?://forge\.puppetlabs\.com}
|
95
|
+
module_repository = "https://forgeapi.puppetlabs.com"
|
96
|
+
warn { "Replacing Puppet Forge API URL to use v3 #{module_repository} as required by your client version #{Librarian::Puppet.puppet_version}" }
|
97
|
+
end
|
98
|
+
|
99
|
+
m = module_repository.match(%r{^http(s)?://forge(api)?\.puppetlabs\.com})
|
100
|
+
if Forge.client_api_version() == 1 and m
|
101
|
+
ssl = m[1]
|
102
|
+
# Puppet 2.7 can't handle the 302 returned by the https url, so stick to http
|
103
|
+
if ssl and Librarian::Puppet::puppet_gem_version < Gem::Version.create('3.0.0')
|
104
|
+
warn { "Using plain http as your version of Puppet #{Librarian::Puppet::puppet_gem_version} can't download from forge.puppetlabs.com using https" }
|
105
|
+
ssl = nil
|
106
|
+
end
|
107
|
+
module_repository = "http#{ssl}://forge.puppetlabs.com"
|
108
|
+
end
|
109
|
+
|
110
|
+
command = %W{puppet module install --version #{version} --target-dir}
|
111
|
+
command.push(*[path.to_s, "--module_repository", module_repository, "--modulepath", path.to_s, "--module_working_dir", path.to_s, "--ignore-dependencies", target])
|
112
|
+
debug { "Executing puppet module install for #{name} #{version}: #{command.join(" ").gsub(module_repository, source.to_s)}" }
|
113
|
+
|
114
|
+
begin
|
115
|
+
Librarian::Posix.run!(command)
|
116
|
+
rescue Posix::CommandFailure => e
|
117
|
+
# Rollback the directory if the puppet module had an error
|
118
|
+
begin
|
119
|
+
path.unlink
|
120
|
+
rescue => u
|
121
|
+
debug("Unable to rollback path #{path}: #{u}")
|
122
|
+
end
|
123
|
+
tar = Dir[File.join(path.to_s, "**/*.tar.gz")]
|
124
|
+
msg = ""
|
125
|
+
if e.message =~ /Unexpected EOF in archive/ and !tar.empty?
|
126
|
+
file = tar.first
|
127
|
+
msg = " (looks like an incomplete download of #{file})"
|
128
|
+
end
|
129
|
+
raise Error, "Error executing puppet module install#{msg}. Check that this command succeeds:\n#{command.join(" ")}\nError:\n#{e.message}"
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
def check_puppet_module_options
|
135
|
+
min_version = Gem::Version.create('2.7.13')
|
136
|
+
|
137
|
+
if Librarian::Puppet.puppet_gem_version < min_version
|
138
|
+
raise Error, "To get modules from the forge, we use the puppet faces module command. For this you need at least puppet version 2.7.13 and you have #{Librarian::Puppet.puppet_version}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def vendor_cache(name, version)
|
143
|
+
url = url(name, version)
|
144
|
+
path = vendored_path(name, version).to_s
|
145
|
+
debug { "Downloading #{url} into #{path}"}
|
146
|
+
environment.vendor!
|
147
|
+
File.open(path, 'wb') do |f|
|
148
|
+
URI.open(url, 'rb') do |input|
|
149
|
+
f.write(input.read)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'librarian/puppet/source/forge/repo'
|
4
|
+
|
5
|
+
module Librarian
|
6
|
+
module Puppet
|
7
|
+
module Source
|
8
|
+
class Forge
|
9
|
+
class RepoV1 < Librarian::Puppet::Source::Forge::Repo
|
10
|
+
|
11
|
+
def initialize(source, name)
|
12
|
+
super(source, name)
|
13
|
+
# API returned data for this module including all versions and dependencies, indexed by module name
|
14
|
+
# from https://forge.puppetlabs.com/api/v1/releases.json?module=#{name}
|
15
|
+
@api_data = nil
|
16
|
+
# API returned data for this module and a specific version, indexed by version
|
17
|
+
# from https://forge.puppetlabs.com/api/v1/releases.json?module=#{name}&version=#{version}
|
18
|
+
@api_version_data = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_versions
|
22
|
+
api_data(name).map { |r| r['version'] }.reverse
|
23
|
+
end
|
24
|
+
|
25
|
+
def dependencies(version)
|
26
|
+
api_version_data(name, version)['dependencies']
|
27
|
+
end
|
28
|
+
|
29
|
+
def url(name, version)
|
30
|
+
info = api_version_data(name, version)
|
31
|
+
"#{source}#{info[name].first['file']}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# convert organization/modulename to organization-modulename
|
37
|
+
def normalize_dependencies(data)
|
38
|
+
return nil if data.nil?
|
39
|
+
# convert organization/modulename to organization-modulename
|
40
|
+
data.keys.each do |m|
|
41
|
+
if m =~ %r{.*/.*}
|
42
|
+
data[normalize_name(m)] = data[m]
|
43
|
+
data.delete(m)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
data
|
47
|
+
end
|
48
|
+
|
49
|
+
# get and cache the API data for a specific module with all its versions and dependencies
|
50
|
+
def api_data(module_name)
|
51
|
+
return @api_data[module_name] if @api_data
|
52
|
+
# call API and cache data
|
53
|
+
@api_data = normalize_dependencies(api_call(module_name))
|
54
|
+
if @api_data.nil?
|
55
|
+
raise Error, "Unable to find module '#{name}' on #{source}"
|
56
|
+
end
|
57
|
+
@api_data[module_name]
|
58
|
+
end
|
59
|
+
|
60
|
+
# get and cache the API data for a specific module and version
|
61
|
+
def api_version_data(module_name, version)
|
62
|
+
# if we already got all the versions, find in cached data
|
63
|
+
return @api_data[module_name].detect{|x| x['version'] == version.to_s} if @api_data
|
64
|
+
# otherwise call the api for this version if not cached already
|
65
|
+
@api_version_data[version] = normalize_dependencies(api_call(name, version)) if @api_version_data[version].nil?
|
66
|
+
@api_version_data[version]
|
67
|
+
end
|
68
|
+
|
69
|
+
def api_call(module_name, version=nil)
|
70
|
+
url = source.uri.clone
|
71
|
+
url.path += "#{'/' if url.path.empty? or url.path[-1] != '/'}api/v1/releases.json"
|
72
|
+
url.query = "module=#{module_name.sub('-','/')}" # v1 API expects "organization/module"
|
73
|
+
url.query += "&version=#{version}" unless version.nil?
|
74
|
+
debug { "Querying Forge API for module #{name}#{" and version #{version}" unless version.nil?}: #{url}" }
|
75
|
+
|
76
|
+
begin
|
77
|
+
data = URI.open(url) {|f| f.read}
|
78
|
+
JSON.parse(data)
|
79
|
+
rescue OpenURI::HTTPError => e
|
80
|
+
case e.io.status[0].to_i
|
81
|
+
when 404,410
|
82
|
+
nil
|
83
|
+
else
|
84
|
+
raise e, "Error requesting #{url}: #{e.to_s}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'librarian/puppet/source/forge/repo'
|
2
|
+
require 'puppet_forge'
|
3
|
+
require 'librarian/puppet/version'
|
4
|
+
|
5
|
+
module Librarian
|
6
|
+
module Puppet
|
7
|
+
module Source
|
8
|
+
class Forge
|
9
|
+
class RepoV3 < Librarian::Puppet::Source::Forge::Repo
|
10
|
+
|
11
|
+
PuppetForge.user_agent = "librarian-puppet/#{Librarian::Puppet::VERSION}"
|
12
|
+
|
13
|
+
def initialize(source, name)
|
14
|
+
PuppetForge.host = source.uri.clone
|
15
|
+
super(source, name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_versions
|
19
|
+
get_module.releases.select{|r| r.deleted_at.nil?}.map{|r| r.version}
|
20
|
+
end
|
21
|
+
|
22
|
+
def dependencies(version)
|
23
|
+
array = get_release(version).metadata[:dependencies].map{|d| [d[:name], d[:version_requirement]]}
|
24
|
+
Hash[*array.flatten(1)]
|
25
|
+
end
|
26
|
+
|
27
|
+
def url(name, version)
|
28
|
+
if name == "#{get_module().owner.username}/#{get_module().name}"
|
29
|
+
release = get_release(version)
|
30
|
+
else
|
31
|
+
# should never get here as we use one repo object for each module (to be changed in the future)
|
32
|
+
debug { "Looking up url for #{name}@#{version}" }
|
33
|
+
release = PuppetForge::V3::Release.find("#{name}-#{version}")
|
34
|
+
end
|
35
|
+
"#{source}#{release.file_uri}"
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def get_module
|
41
|
+
begin
|
42
|
+
@module ||= PuppetForge::V3::Module.find(name)
|
43
|
+
rescue Faraday::ResourceNotFound => e
|
44
|
+
raise(Error, "Unable to find module '#{name}' on #{source}")
|
45
|
+
end
|
46
|
+
@module
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_release(version)
|
50
|
+
release = get_module.releases.find{|r| r.version == version.to_s}
|
51
|
+
if release.nil?
|
52
|
+
versions = get_module.releases.map{|r| r.version}
|
53
|
+
raise Error, "Unable to find version '#{version}' for module '#{name}' on #{source} amongst #{versions}"
|
54
|
+
end
|
55
|
+
release
|
56
|
+
end
|
57
|
+
|
58
|
+
def cache_version_unpacked!(version)
|
59
|
+
path = version_unpacked_cache_path(version)
|
60
|
+
return if path.directory?
|
61
|
+
|
62
|
+
path.mkpath
|
63
|
+
|
64
|
+
target = vendored?(name, version) ? vendored_path(name, version).to_s : name
|
65
|
+
|
66
|
+
# can't pass the default v3 forge url (http://forgeapi.puppetlabs.com)
|
67
|
+
# to clients that use the v1 API (https://forge.puppet.com)
|
68
|
+
# nor the other way around
|
69
|
+
module_repository = source.uri.to_s
|
70
|
+
|
71
|
+
if Forge.client_api_version() > 1 and module_repository =~ %r{^http(s)?://forge\.puppetlabs\.com}
|
72
|
+
module_repository = "https://forgeapi.puppetlabs.com"
|
73
|
+
warn { "Replacing Puppet Forge API URL to use v3 #{module_repository} as required by your client version #{Librarian::Puppet.puppet_version}" }
|
74
|
+
end
|
75
|
+
|
76
|
+
tar_dst = environment.tmp_path.join("#{target}-#{version}.tar.gz")
|
77
|
+
dest_dir = path.join(module_name(name))
|
78
|
+
tmp_dir = environment.tmp_path.join("forge").to_s
|
79
|
+
|
80
|
+
begin
|
81
|
+
release = get_release(version)
|
82
|
+
release.download(tar_dst)
|
83
|
+
release.verify(tar_dst)
|
84
|
+
PuppetForge::Unpacker.unpack(tar_dst, dest_dir, tmp_dir)
|
85
|
+
rescue => e
|
86
|
+
# Rollback the directory if the puppet module had an error
|
87
|
+
begin
|
88
|
+
path.unlink
|
89
|
+
rescue => u
|
90
|
+
debug("Unable to rollback path #{path}: #{u}")
|
91
|
+
end
|
92
|
+
raise Error, "Error downloading module from forge. \nError:\n#{e.message}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|