anthill-librarian-puppet 3.0.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.md +337 -0
- data/bin/librarian-puppet +7 -0
- data/lib/librarian/puppet.rb +36 -0
- data/lib/librarian/puppet/action.rb +2 -0
- data/lib/librarian/puppet/action/install.rb +26 -0
- data/lib/librarian/puppet/action/resolve.rb +21 -0
- data/lib/librarian/puppet/cli.rb +110 -0
- data/lib/librarian/puppet/dependency.rb +18 -0
- data/lib/librarian/puppet/dsl.rb +92 -0
- data/lib/librarian/puppet/environment.rb +66 -0
- data/lib/librarian/puppet/extension.rb +9 -0
- data/lib/librarian/puppet/lockfile.rb +39 -0
- data/lib/librarian/puppet/source.rb +4 -0
- data/lib/librarian/puppet/source/forge.rb +181 -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 +62 -0
- data/lib/librarian/puppet/source/git.rb +74 -0
- data/lib/librarian/puppet/source/githubtarball.rb +130 -0
- data/lib/librarian/puppet/source/githubtarball/repo.rb +172 -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 +38 -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
- metadata +232 -0
@@ -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.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(" ")}" }
|
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
|
+
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 = 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,62 @@
|
|
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
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'librarian/source/git'
|
2
|
+
require 'librarian/puppet/source/local'
|
3
|
+
|
4
|
+
module Librarian
|
5
|
+
module Source
|
6
|
+
class Git
|
7
|
+
class Repository
|
8
|
+
def hash_from(remote, reference)
|
9
|
+
branch_names = remote_branch_names[remote]
|
10
|
+
if branch_names.include?(reference)
|
11
|
+
reference = "#{remote}/#{reference}"
|
12
|
+
end
|
13
|
+
|
14
|
+
command = %W(rev-parse #{reference}^{commit} --quiet)
|
15
|
+
run!(command, :chdir => true).strip
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Puppet
|
22
|
+
module Source
|
23
|
+
class Git < Librarian::Source::Git
|
24
|
+
include Local
|
25
|
+
include Librarian::Puppet::Util
|
26
|
+
|
27
|
+
def cache!
|
28
|
+
return vendor_checkout! if vendor_cached?
|
29
|
+
|
30
|
+
if environment.local?
|
31
|
+
raise Error, "Could not find a local copy of #{uri}#{" at #{sha}" unless sha.nil?}."
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
super
|
36
|
+
rescue Librarian::Posix::CommandFailure => e
|
37
|
+
raise Error, "Could not checkout #{uri}#{" at #{sha}" unless sha.nil?}: #{e}"
|
38
|
+
end
|
39
|
+
|
40
|
+
cache_in_vendor(repository.path) if environment.vendor?
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def vendor_tar
|
46
|
+
environment.vendor_source.join("#{sha}.tar")
|
47
|
+
end
|
48
|
+
|
49
|
+
def vendor_tgz
|
50
|
+
environment.vendor_source.join("#{sha}.tar.gz")
|
51
|
+
end
|
52
|
+
|
53
|
+
def vendor_cached?
|
54
|
+
vendor_tgz.exist?
|
55
|
+
end
|
56
|
+
|
57
|
+
def vendor_checkout!
|
58
|
+
repository.path.rmtree if repository.path.exist?
|
59
|
+
repository.path.mkpath
|
60
|
+
|
61
|
+
Librarian::Posix.run!(%W{tar xzf #{vendor_tgz}}, :chdir => repository.path.to_s)
|
62
|
+
|
63
|
+
repository_cached!
|
64
|
+
end
|
65
|
+
|
66
|
+
def cache_in_vendor(tmp_path)
|
67
|
+
Librarian::Posix.run!(%W{git archive -o #{vendor_tar} #{sha}}, :chdir => tmp_path.to_s)
|
68
|
+
Librarian::Posix.run!(%W{gzip #{vendor_tar}}, :chdir => tmp_path.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'librarian/puppet/util'
|
3
|
+
require 'librarian/puppet/source/githubtarball/repo'
|
4
|
+
|
5
|
+
module Librarian
|
6
|
+
module Puppet
|
7
|
+
module Source
|
8
|
+
class GitHubTarball
|
9
|
+
include Librarian::Puppet::Util
|
10
|
+
|
11
|
+
class << self
|
12
|
+
LOCK_NAME = 'GITHUBTARBALL'
|
13
|
+
|
14
|
+
def lock_name
|
15
|
+
LOCK_NAME
|
16
|
+
end
|
17
|
+
|
18
|
+
def from_lock_options(environment, options)
|
19
|
+
new(environment, options[:remote], options.reject { |k, v| k == :remote })
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_spec_args(environment, uri, options)
|
23
|
+
recognised_options = []
|
24
|
+
unrecognised_options = options.keys - recognised_options
|
25
|
+
unless unrecognised_options.empty?
|
26
|
+
raise Error, "unrecognised options: #{unrecognised_options.join(", ")}"
|
27
|
+
end
|
28
|
+
|
29
|
+
new(environment, uri, options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_accessor :environment
|
34
|
+
private :environment=
|
35
|
+
attr_reader :uri
|
36
|
+
|
37
|
+
def initialize(environment, uri, options = {})
|
38
|
+
self.environment = environment
|
39
|
+
@uri = URI::parse(uri)
|
40
|
+
@cache_path = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
clean_uri(uri).to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
other &&
|
49
|
+
self.class == other.class &&
|
50
|
+
self.uri == other.uri
|
51
|
+
end
|
52
|
+
|
53
|
+
alias :eql? :==
|
54
|
+
|
55
|
+
def hash
|
56
|
+
self.to_s.hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_spec_args
|
60
|
+
[clean_uri(uri).to_s, {}]
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_lock_options
|
64
|
+
{:remote => clean_uri(uri).to_s}
|
65
|
+
end
|
66
|
+
|
67
|
+
def pinned?
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
def unpin!
|
72
|
+
end
|
73
|
+
|
74
|
+
def install!(manifest)
|
75
|
+
manifest.source == self or raise ArgumentError
|
76
|
+
|
77
|
+
debug { "Installing #{manifest}" }
|
78
|
+
|
79
|
+
name = manifest.name
|
80
|
+
version = manifest.version
|
81
|
+
install_path = install_path(name)
|
82
|
+
repo = repo(name)
|
83
|
+
|
84
|
+
repo.install_version! version, install_path
|
85
|
+
end
|
86
|
+
|
87
|
+
def manifest(name, version, dependencies)
|
88
|
+
manifest = Manifest.new(self, name)
|
89
|
+
manifest.version = version
|
90
|
+
manifest.dependencies = dependencies
|
91
|
+
manifest
|
92
|
+
end
|
93
|
+
|
94
|
+
def cache_path
|
95
|
+
@cache_path ||= begin
|
96
|
+
environment.cache_path.join("source/puppet/githubtarball/#{uri.host}#{uri.path}")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def install_path(name)
|
101
|
+
environment.install_path.join(module_name(name))
|
102
|
+
end
|
103
|
+
|
104
|
+
def fetch_version(name, version_uri)
|
105
|
+
versions = repo(name).versions
|
106
|
+
if versions.include? version_uri
|
107
|
+
version_uri
|
108
|
+
else
|
109
|
+
versions.first
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def fetch_dependencies(name, version, version_uri)
|
114
|
+
{}
|
115
|
+
end
|
116
|
+
|
117
|
+
def manifests(name)
|
118
|
+
repo(name).manifests
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def repo(name)
|
124
|
+
@repo ||= {}
|
125
|
+
@repo[name] ||= Repo.new(self, name)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|