anthill-librarian-puppet 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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