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.
@@ -0,0 +1,172 @@
1
+ require 'uri'
2
+ require 'net/https'
3
+ require 'open-uri'
4
+ require 'json'
5
+
6
+ require 'librarian/puppet/version'
7
+ require 'librarian/puppet/source/repo'
8
+
9
+ module Librarian
10
+ module Puppet
11
+ module Source
12
+ class GitHubTarball
13
+ class Repo < Librarian::Puppet::Source::Repo
14
+ include Librarian::Puppet::Util
15
+
16
+ TOKEN_KEY = 'GITHUB_API_TOKEN'
17
+
18
+ def versions
19
+ return @versions if @versions
20
+ data = api_call("/repos/#{source.uri}/tags")
21
+ if data.nil?
22
+ raise Error, "Unable to find module '#{source.uri}' on https://github.com"
23
+ end
24
+
25
+ all_versions = data.map { |r| r['name'].gsub(/^v/, '') }.sort.reverse
26
+
27
+ all_versions.delete_if do |version|
28
+ version !~ /\A\d+\.\d+(\.\d+.*)?\z/
29
+ end
30
+
31
+ @versions = all_versions.compact
32
+ debug { " Module #{name} found versions: #{@versions.join(", ")}" }
33
+ @versions
34
+ end
35
+
36
+ def manifests
37
+ versions.map do |version|
38
+ Manifest.new(source, name, version)
39
+ end
40
+ end
41
+
42
+ def install_version!(version, install_path)
43
+ if environment.local? && !vendored?(vendored_name, version)
44
+ raise Error, "Could not find a local copy of #{source.uri} at #{version}."
45
+ end
46
+
47
+ vendor_cache(source.uri.to_s, version) unless vendored?(vendored_name, version)
48
+
49
+ cache_version_unpacked! version
50
+
51
+ if install_path.exist? && rsync? != true
52
+ install_path.rmtree
53
+ end
54
+
55
+ unpacked_path = version_unpacked_cache_path(version).children.first
56
+ cp_r(unpacked_path, install_path)
57
+ end
58
+
59
+ def cache_version_unpacked!(version)
60
+ path = version_unpacked_cache_path(version)
61
+ return if path.directory?
62
+
63
+ path.mkpath
64
+
65
+ target = vendored?(vendored_name, version) ? vendored_path(vendored_name, version) : name
66
+
67
+ Librarian::Posix.run!(%W{tar xzf #{target} -C #{path}})
68
+ end
69
+
70
+ def vendor_cache(name, version)
71
+ clean_up_old_cached_versions(vendored_name(name))
72
+
73
+ url = "https://api.github.com/repos/#{name}/tarball/#{version}"
74
+ add_api_token_to_url(url)
75
+
76
+ environment.vendor!
77
+ File.open(vendored_path(vendored_name(name), version).to_s, 'wb') do |f|
78
+ begin
79
+ debug { "Downloading <#{url}> to <#{f.path}>" }
80
+ open(url,
81
+ "User-Agent" => "librarian-puppet v#{Librarian::Puppet::VERSION}") do |res|
82
+ while buffer = res.read(8192)
83
+ f.write(buffer)
84
+ end
85
+ end
86
+ rescue OpenURI::HTTPError => e
87
+ raise e, "Error requesting <#{url}>: #{e.to_s}"
88
+ end
89
+ end
90
+ end
91
+
92
+ def clean_up_old_cached_versions(name)
93
+ Dir["#{environment.vendor_cache}/#{name}*.tar.gz"].each do |old_version|
94
+ FileUtils.rm old_version
95
+ end
96
+ end
97
+
98
+ def token_key_value
99
+ ENV[TOKEN_KEY]
100
+ end
101
+
102
+ def token_key_nil?
103
+ token_key_value.nil? || token_key_value.empty?
104
+ end
105
+
106
+ def add_api_token_to_url url
107
+ if token_key_nil?
108
+ debug { "#{TOKEN_KEY} environment value is empty or missing" }
109
+ elsif url.include? "?"
110
+ url << "&access_token=#{ENV[TOKEN_KEY]}"
111
+ else
112
+ url << "?access_token=#{ENV[TOKEN_KEY]}"
113
+ end
114
+ url
115
+ end
116
+
117
+ private
118
+
119
+ def api_call(path)
120
+ tags = []
121
+ url = "https://api.github.com#{path}?page=1&per_page=100"
122
+ while true do
123
+ debug { " Module #{name} getting tags at: #{url}" }
124
+ add_api_token_to_url(url)
125
+ response = http_get(url, :headers => {
126
+ "User-Agent" => "librarian-puppet v#{Librarian::Puppet::VERSION}"
127
+ })
128
+
129
+ code, data = response.code.to_i, response.body
130
+
131
+ if code == 200
132
+ tags.concat JSON.parse(data)
133
+ else
134
+ begin
135
+ message = JSON.parse(data)['message']
136
+ if code == 403 && message && message.include?('API rate limit exceeded')
137
+ raise Error, message + " -- increase limit by authenticating via #{TOKEN_KEY}=your-token"
138
+ elsif message
139
+ raise Error, "Error fetching #{url}: [#{code}] #{message}"
140
+ end
141
+ rescue JSON::ParserError
142
+ # response does not return json
143
+ end
144
+ raise Error, "Error fetching #{url}: [#{code}] #{response.body}"
145
+ end
146
+
147
+ # next page
148
+ break if response["link"].nil?
149
+ next_link = response["link"].split(",").select{|l| l.match /rel=.*next.*/}
150
+ break if next_link.empty?
151
+ url = next_link.first.match(/<(.*)>/)[1]
152
+ end
153
+ return tags
154
+ end
155
+
156
+ def http_get(url, options)
157
+ uri = URI.parse(url)
158
+ http = Net::HTTP.new(uri.host, uri.port)
159
+ http.use_ssl = true
160
+ request = Net::HTTP::Get.new(uri.request_uri)
161
+ options[:headers].each { |k, v| request.add_field k, v }
162
+ http.request(request)
163
+ end
164
+
165
+ def vendored_name(name = source.uri.to_s)
166
+ name.sub('/','-')
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,176 @@
1
+ require 'librarian/puppet/util'
2
+
3
+ module Librarian
4
+ module Puppet
5
+ module Source
6
+ module Local
7
+ include Librarian::Puppet::Util
8
+
9
+ def install!(manifest)
10
+ manifest.source == self or raise ArgumentError
11
+
12
+ debug { "Installing #{manifest}" }
13
+
14
+ name, version = manifest.name, manifest.version
15
+ found_path = found_path(name)
16
+ raise Error, "Path for #{name} doesn't contain a puppet module" if found_path.nil?
17
+
18
+ unless name.include? '/' or name.include? '-'
19
+ warn { "Invalid module name '#{name}', you should qualify it with 'ORGANIZATION-#{name}' for resolution to work correctly" }
20
+ end
21
+
22
+ install_path = environment.install_path.join(module_name(name))
23
+ if install_path.exist? && rsync? != true
24
+ debug { "Deleting #{relative_path_to(install_path)}" }
25
+ install_path.rmtree
26
+ end
27
+
28
+ install_perform_step_copy!(found_path, install_path)
29
+ end
30
+
31
+ def fetch_version(name, extra)
32
+ cache!
33
+ found_path = found_path(name)
34
+ module_version
35
+ end
36
+
37
+ def fetch_dependencies(name, version, extra)
38
+ dependencies = Set.new
39
+
40
+ if specfile?
41
+ spec = environment.dsl(Pathname(specfile))
42
+ dependencies.merge spec.dependencies
43
+ end
44
+
45
+ parsed_metadata['dependencies'].each do |d|
46
+ gem_requirement = Librarian::Dependency::Requirement.new(d['version_requirement']).to_gem_requirement
47
+ new_dependency = Dependency.new(d['name'], gem_requirement, forge_source)
48
+ dependencies << new_dependency
49
+ end
50
+
51
+ dependencies
52
+ end
53
+
54
+ def forge_source
55
+ Forge.default
56
+ end
57
+
58
+ private
59
+
60
+ # Naming this method 'version' causes an exception to be raised.
61
+ def module_version
62
+ if parsed_metadata['version']
63
+ parsed_metadata['version']
64
+ else
65
+ warn { "Module #{to_s} does not have version, defaulting to 0.0.1" }
66
+ '0.0.1'
67
+ end
68
+ end
69
+
70
+ def require_puppet
71
+ begin
72
+ require 'puppet'
73
+ require 'puppet/module_tool'
74
+ rescue LoadError
75
+ $stderr.puts <<-EOF
76
+ Unable to load puppet, the puppet gem is required for :git and :path source.
77
+ Install it with: gem install puppet
78
+ EOF
79
+ exit 1
80
+ end
81
+ true
82
+ end
83
+
84
+ def evaluate_modulefile(modulefile)
85
+ @@require_puppet ||= require_puppet
86
+
87
+ metadata = ::Puppet::ModuleTool::Metadata.new
88
+
89
+ # Puppet 4 does not have the class
90
+ unless defined? ::Puppet::ModuleTool::ModulefileReader
91
+ warn { "Can't parse Modulefile in Puppet >= 4.0 and you are using #{Librarian::Puppet::puppet_version}. Ignoring dependencies in #{modulefile}" }
92
+ return metadata
93
+ end
94
+
95
+ begin
96
+ ::Puppet::ModuleTool::ModulefileReader.evaluate(metadata, modulefile)
97
+ raise SyntaxError, "Missing version" unless metadata.version
98
+ rescue ArgumentError, SyntaxError => error
99
+ warn { "Unable to parse #{modulefile}, ignoring: #{error}" }
100
+ if metadata.respond_to? :version=
101
+ metadata.version = '0.0.1' # puppet < 3.6
102
+ else
103
+ metadata.update({'version' => '0.0.1'}) # puppet >= 3.6
104
+ end
105
+ end
106
+ metadata
107
+ end
108
+
109
+ def parsed_metadata
110
+ if @metadata.nil?
111
+ @metadata = if metadata?
112
+ begin
113
+ JSON.parse(File.read(metadata))
114
+ rescue JSON::ParserError => e
115
+ raise Error, "Unable to parse json file #{metadata}: #{e}"
116
+ end
117
+ elsif modulefile?
118
+ # translate Modulefile to metadata.json
119
+ evaluated = evaluate_modulefile(modulefile)
120
+ {
121
+ 'version' => evaluated.version,
122
+ 'dependencies' => evaluated.dependencies.map do |dependency|
123
+ {
124
+ 'name' => dependency.instance_variable_get(:@full_module_name),
125
+ 'version_requirement' => dependency.instance_variable_get(:@version_requirement)
126
+ }
127
+ end
128
+ }
129
+ else
130
+ {}
131
+ end
132
+ @metadata['dependencies'] ||= []
133
+ end
134
+ @metadata
135
+ end
136
+
137
+ def modulefile
138
+ File.join(filesystem_path, 'Modulefile')
139
+ end
140
+
141
+ def modulefile?
142
+ File.exists?(modulefile)
143
+ end
144
+
145
+ def metadata
146
+ File.join(filesystem_path, 'metadata.json')
147
+ end
148
+
149
+ def metadata?
150
+ File.exists?(metadata)
151
+ end
152
+
153
+ def specfile
154
+ File.join(filesystem_path, environment.specfile_name)
155
+ end
156
+
157
+ def specfile?
158
+ File.exists?(specfile)
159
+ end
160
+
161
+ def install_perform_step_copy!(found_path, install_path)
162
+ debug { "Copying #{relative_path_to(found_path)} to #{relative_path_to(install_path)}" }
163
+ cp_r(found_path, install_path)
164
+ end
165
+
166
+ def manifest?(name, path)
167
+ return true if path.join('manifests').exist?
168
+ return true if path.join('lib').join('puppet').exist?
169
+ return true if path.join('lib').join('facter').exist?
170
+ debug { "Could not find manifests, lib/puppet or lib/facter under #{path}, maybe it is not a puppet module" }
171
+ true
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,12 @@
1
+ require 'librarian/source/path'
2
+ require 'librarian/puppet/source/local'
3
+
4
+ module Librarian
5
+ module Puppet
6
+ module Source
7
+ class Path < Librarian::Source::Path
8
+ include Local
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ # parent class for githubtarball and forge source Repos
2
+ module Librarian
3
+ module Puppet
4
+ module Source
5
+ class Repo
6
+
7
+ attr_accessor :source, :name
8
+ private :source=, :name=
9
+
10
+ def initialize(source, name)
11
+ self.source = source
12
+ self.name = name
13
+ end
14
+
15
+ def environment
16
+ source.environment
17
+ end
18
+
19
+ def cache_path
20
+ @cache_path ||= source.cache_path.join(name)
21
+ end
22
+
23
+ def version_unpacked_cache_path(version)
24
+ cache_path.join(version.to_s)
25
+ end
26
+
27
+ def vendored?(name, version)
28
+ vendored_path(name, version).exist?
29
+ end
30
+
31
+ def vendored_path(name, version)
32
+ environment.vendor_cache.join("#{name}-#{version}.tar.gz")
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ #^syntax detection
3
+
4
+ forge "https://forgeapi.puppetlabs.com"
5
+
6
+ # use dependencies defined in metadata.json
7
+ metadata
8
+
9
+ # use dependencies defined in Modulefile
10
+ # modulefile
11
+
12
+ # A module from the Puppet Forge
13
+ # mod 'puppetlabs-stdlib'
14
+
15
+ # A module from git
16
+ # mod 'puppetlabs-ntp',
17
+ # :git => 'git://github.com/puppetlabs/puppetlabs-ntp.git'
18
+
19
+ # A module from a git branch/tag
20
+ # mod 'puppetlabs-apt',
21
+ # :git => 'https://github.com/puppetlabs/puppetlabs-apt.git',
22
+ # :ref => '1.4.x'
23
+
24
+ # A module from Github pre-packaged tarball
25
+ # mod 'puppetlabs-apache', '0.6.0', :github_tarball => 'puppetlabs/puppetlabs-apache'
@@ -0,0 +1,78 @@
1
+ require 'rsync'
2
+
3
+ module Librarian
4
+ module Puppet
5
+
6
+ module Util
7
+
8
+ def debug(*args, &block)
9
+ environment.logger.debug(*args, &block)
10
+ end
11
+ def info(*args, &block)
12
+ environment.logger.info(*args, &block)
13
+ end
14
+ def warn(*args, &block)
15
+ environment.logger.warn(*args, &block)
16
+ end
17
+
18
+ def rsync?
19
+ environment.config_db.local['rsync'] == 'true'
20
+ end
21
+
22
+ # workaround Issue #173 FileUtils.cp_r will fail if there is a symlink that points to a missing file
23
+ # or when the symlink is copied before the target file when preserve is true
24
+ # see also https://tickets.opscode.com/browse/CHEF-833
25
+ #
26
+ # If the rsync configuration parameter is set, use rsync instead of FileUtils
27
+ def cp_r(src, dest)
28
+ if rsync?
29
+ if Gem.win_platform?
30
+ src_clean = "#{src}".gsub(/^([a-z])\:/i,'/cygdrive/\1')
31
+ dest_clean = "#{dest}".gsub(/^([a-z])\:/i,'/cygdrive/\1')
32
+ else
33
+ src_clean = src
34
+ dest_clean = dest
35
+ end
36
+ debug { "Copying #{src_clean}/ to #{dest_clean}/ with rsync -avz --delete" }
37
+ result = Rsync.run(File.join(src_clean, "/"), File.join(dest_clean, "/"), ['-avz', '--delete'])
38
+ if result.success?
39
+ debug { "Rsync from #{src_clean}/ to #{dest_clean}/ successfull" }
40
+ else
41
+ msg = "Failed to rsync from #{src_clean}/ to #{dest_clean}/: " + result.error
42
+ raise Error, msg
43
+ end
44
+ else
45
+ begin
46
+ FileUtils.cp_r(src, dest, :preserve => true)
47
+ rescue Errno::ENOENT, Errno::EACCES
48
+ debug { "Failed to copy from #{src} to #{dest} preserving file types, trying again without preserving them" }
49
+ FileUtils.rm_rf(dest)
50
+ FileUtils.cp_r(src, dest)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Remove user and password from a URI object
56
+ def clean_uri(uri)
57
+ new_uri = uri.clone
58
+ new_uri.user = nil
59
+ new_uri.password = nil
60
+ new_uri
61
+ end
62
+
63
+ # normalize module name to use organization-module instead of organization/module
64
+ def normalize_name(name)
65
+ name.sub('/','-')
66
+ end
67
+
68
+ # get the module name from organization-module
69
+ def module_name(name)
70
+ # module name can't have dashes, so let's assume it is everything after the last dash
71
+ name.rpartition('-').last
72
+ end
73
+
74
+ # deprecated
75
+ alias :organization_name :module_name
76
+ end
77
+ end
78
+ end