puppet-library 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6c1ea499735a82bacf4051bd1b4ea24f2003d613
4
- data.tar.gz: ad4e79023760adaf7bc96becfa61d9403f2c60be
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZDc4NjNkZDQyZTlhNWMzZWIwZDhmMTM1MzY1ZTg4NzUyNWQyYzY2OQ==
5
+ data.tar.gz: !binary |-
6
+ MzA4N2JiOGU0MTkzMTBmZGUyYzhhOTFiNWQxZGViOGU0YTY5ODk3OA==
5
7
  SHA512:
6
- metadata.gz: ebe0288bdc97d3df601dc7f1d523d49d3884662af2b756aac2a7634219175a7b7f6035b3a3ffd0bafcfd87f0c9c3c5eb847ff12409a9aca7daf4de24da4b1141
7
- data.tar.gz: 81466a2f9550252a75f7f6d98d941df618eb0b611a52a2d7df0fb30e85b825dcdd0399ed3876889189417e07e14b650cb219f31d0cdb2e33b8d22cf597ce32e5
8
+ metadata.gz: !binary |-
9
+ NWUzMTQ5MDM2ZWEwOWY4MzcwYTI1NGRkNjJiNWI3ZDJiN2I0OTg5MGMwMWEz
10
+ MmY0ZmRhNTQ4ZWI5YWQ2ZWUxMGUxNDQ0YjhjYzMzMmJmMTBjNWRmMTlkM2M3
11
+ M2Y4YTU0NzQwOThjM2E0YTU2MzIzYWM2M2U4YmFmNDkyYzBkMDY=
12
+ data.tar.gz: !binary |-
13
+ MDBmNzBkZDYzZWZkZWU5YWE0ODhkZDNmMDQ5ZmRkODMxNjJjYTQ4Y2Q2M2U3
14
+ Njc3M2E3Zjk1NGE4MGM2ZWJmMDllMThiM2IwMDE3M2EzYjAxNDg2OTNlOGM0
15
+ ZGYyNGNhNjJlOWI5MTRlY2QzMjk0YzY5NmU1ZmFlNTNjNTg0ZDc=
data/.travis.yml CHANGED
@@ -16,13 +16,16 @@
16
16
 
17
17
  language: ruby
18
18
  before_install:
19
- - gem update --system 2.1.11
19
+ - gem update --system ${RUBYGEMS_VERSION}
20
20
  - gem --version
21
21
  rvm:
22
22
  - 1.8.7
23
23
  - 1.9.3
24
24
  - 2.0.0
25
25
  - 2.1.0
26
- matrix:
27
- allow_failures:
28
- - rvm: 2.1.0
26
+ env:
27
+ - RUBYGEMS_VERSION=2.1.11
28
+ - RUBYGEMS_VERSION=1.8.24
29
+ #matrix:
30
+ # allow_failures:
31
+ # - rvm: 2.1.0
data/CHANGELOG.yml CHANGED
@@ -28,4 +28,8 @@
28
28
  changes:
29
29
  - Support more specific module version queries using 'version' parameter
30
30
  - Fixed bug on older versions of RubyGems
31
+ - tag: v0.3.0
32
+ changes:
33
+ - Added module search
34
+ - Improved performance of proxy
31
35
 
@@ -15,29 +15,11 @@
15
15
  # You should have received a copy of the GNU General Public License
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
- module PuppetLibrary::ModuleRepo
19
- class Multi
20
- def repos
21
- @repos ||= []
22
- end
18
+ notification :terminal_notifier
23
19
 
24
- def add_repo(repo)
25
- repos << repo
26
- end
27
-
28
- def get_module(author, name, version)
29
- repos.each do |repo|
30
- mod = repo.get_module(author, name, version)
31
- return mod unless mod.nil?
32
- end
33
- return nil
34
- end
35
-
36
- def get_metadata(author, name)
37
- metadata_list = repos.inject([]) do |metadata_list, repo|
38
- metadata_list + repo.get_metadata(author, name)
39
- end
40
- metadata_list.unique_by { |metadata| metadata["version"] }
41
- end
42
- end
20
+ guard 'rspec', :version => 2 do
21
+ watch(%r{^spec/.+_spec\.rb$})
22
+ watch(%r{^lib/puppet_library/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
23
+ watch('spec/spec_helper.rb') { "spec" }
43
24
  end
25
+
data/README.md CHANGED
@@ -74,6 +74,21 @@ See all options
74
74
 
75
75
  $ puppet-library --help
76
76
 
77
+ ## Back-Ends
78
+
79
+ Puppet Library contains built-in support for:
80
+ - serving packaged (`.tar.gz`) modules from a directory (or directories) of your choosing
81
+ - proxying a remote forge (or forges)
82
+ - serving modules from a combination of the above
83
+
84
+ ## Compatibility with other tools
85
+
86
+ Puppet Library currently supports:
87
+ - search with Puppet (`puppet module search apache`)
88
+ - dependency resolution and installation with Puppet (`puppet module install puppetlabs/apache`)
89
+ - dependency resolution and installation with [librarian-puppet](http://librarian-puppet.com)
90
+ - installation with [r10k](https://github.com/adrienthebo/r10k)
91
+
77
92
  ## Contributing
78
93
 
79
94
  1. Fork it
data/Rakefile CHANGED
@@ -19,11 +19,25 @@ require 'bundler/gem_tasks'
19
19
  require 'rspec/core/rake_task'
20
20
  require 'coveralls/rake/task'
21
21
 
22
- if RUBY_VERSION.start_with? "1.8"
23
- # The integration test doesn't work on Ruby 1.8.
24
- DEFAULT_TEST_TASK = :spec
25
- else
22
+ class String
23
+ def green
24
+ "\e[32m#{self}\e[0m"
25
+ end
26
+
27
+ def red
28
+ "\e[31m#{self}\e[0m"
29
+ end
30
+ end
31
+
32
+ def ruby_version_supports_integration_test?(version = RUBY_VERSION)
33
+ # The integration test doesn't work on Ruby 1.8, and Puppet doesn't work on 2.1
34
+ ! /^(1\.8|2\.1)/.match(version)
35
+ end
36
+
37
+ if ruby_version_supports_integration_test?
26
38
  DEFAULT_TEST_TASK = :test
39
+ else
40
+ DEFAULT_TEST_TASK = :spec
27
41
  end
28
42
 
29
43
  Coveralls::RakeTask.new
@@ -31,6 +45,11 @@ Coveralls::RakeTask.new
31
45
  desc "Run the specs"
32
46
  RSpec::Core::RakeTask.new(:spec)
33
47
 
48
+ desc "Run the integration tests"
49
+ RSpec::Core::RakeTask.new(:integration_test) do |rspec|
50
+ rspec.pattern = "test/**/*_integration_test.rb"
51
+ end
52
+
34
53
  desc "Run all the tests"
35
54
  RSpec::Core::RakeTask.new(:test) do |rspec|
36
55
  rspec.pattern = "{spec,test}/**/*_{spec,integration_test}.rb"
@@ -40,7 +59,43 @@ task :default => [DEFAULT_TEST_TASK, 'coveralls:push']
40
59
 
41
60
  desc "Check it works on all local rubies"
42
61
  task :verify do
43
- system "rvm all do rake test"
62
+ versions = %w[1.8 1.9 2.0 2.1]
63
+ puts "\nRunning Specs".green
64
+ spec_results = versions.map do |ruby_version|
65
+ puts "\n- Ruby #{ruby_version}".green
66
+ system "rvm #{ruby_version} do rake spec"
67
+ end
68
+
69
+ puts "\nRunning Integration Tests".green
70
+ integration_test_results = versions.map do |ruby_version|
71
+ puts "\n- Ruby #{ruby_version}".green
72
+ system "rvm #{ruby_version} do rake integration_test"
73
+ end
74
+
75
+ puts "\nResults:\n".green
76
+ results = spec_results.zip(integration_test_results)
77
+ puts "+---------+-------+-------+"
78
+ puts "| Version | Specs | Tests |"
79
+ puts "+---------+-------+-------+"
80
+ versions.zip(results).each do |(version, (spec_result, integration_test_result))|
81
+ v = version
82
+ s = spec_result ? "pass".green : "fail".red
83
+ i = integration_test_result ? "pass".green : "fail".red
84
+ puts "| #{v} | #{s} | #{i} |"
85
+ end
86
+ puts "+---------+-------+-------+"
87
+
88
+ versions.zip(results).each do |(version, (spec_result, integration_test_result))|
89
+ unless spec_result
90
+ fail "Specs failed with Ruby #{version}"
91
+ end
92
+
93
+ if ruby_version_supports_integration_test?(version)
94
+ unless integration_test_result
95
+ fail "Integration tests failed with Ruby #{version}"
96
+ end
97
+ end
98
+ end
44
99
  end
45
100
 
46
101
  desc "Check all files for license headers"
data/config.ru CHANGED
@@ -19,8 +19,8 @@ require 'rubygems'
19
19
  require 'puppet_library'
20
20
 
21
21
  server = PuppetLibrary::Server.set_up do |library|
22
- library.module_repo PuppetLibrary::ModuleRepo::Proxy.new("http://forge.puppetlabs.com")
23
- library.module_repo PuppetLibrary::ModuleRepo::Directory.new("/var/lib/modules")
22
+ library.forge PuppetLibrary::Forge::Proxy.new("http://forge.puppetlabs.com")
23
+ library.forge PuppetLibrary::Forge::Directory.new("/var/lib/modules")
24
24
  end
25
25
 
26
26
  run server
@@ -0,0 +1,183 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # Puppet Library
3
+ # Copyright (C) 2014 drrb
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'puppet_library/forge/search_result'
19
+ require 'puppet_library/util'
20
+
21
+ module PuppetLibrary::Forge
22
+
23
+ class Abstract
24
+ def initialize(module_repo)
25
+ @repo = module_repo
26
+ end
27
+
28
+ def search_modules(query)
29
+ search = Search.new(query)
30
+
31
+ search_results = retrieve_all_metadata.select do |result|
32
+ search.matches? result
33
+ end.sort_by do |result|
34
+ result.version
35
+ end.reverse.map do |result|
36
+ result.to_search_result
37
+ end
38
+
39
+ SearchResult.merge_by_full_name(search_results)
40
+ end
41
+
42
+ def get_module_metadata(author, name)
43
+ modules = retrieve_metadata(author, name)
44
+
45
+ raise ModuleNotFound if modules.empty?
46
+
47
+ module_infos = modules.map { |m| m.to_info }
48
+ module_infos.deep_merge
49
+ end
50
+
51
+ def get_module_metadata_with_dependencies(author, name, version)
52
+ raise ModuleNotFound if retrieve_metadata(author, name).empty?
53
+
54
+ full_name = "#{author}/#{name}"
55
+ versions = collect_dependencies_versions(full_name)
56
+ return versions if version.nil?
57
+
58
+ versions[full_name] = versions[full_name].select do |v|
59
+ Gem::Dependency.new(name, version).match?(name, v["version"])
60
+ end
61
+
62
+ dependencies = versions[full_name].map do |v|
63
+ v["dependencies"].map {|(name, spec)| name}
64
+ end.flatten
65
+ versions = Hash[versions.select do |name, info|
66
+ name == full_name || dependencies.include?(name)
67
+ end]
68
+ return versions
69
+ end
70
+
71
+ def collect_dependencies_versions(module_full_name, metadata = {})
72
+ author, module_name = module_full_name.split "/"
73
+ module_versions = retrieve_metadata(author, module_name)
74
+ metadata[module_full_name] = module_versions.map {|v| v.to_version }
75
+
76
+ dependencies = module_versions.map {|v| v.dependency_names }.flatten
77
+ dependencies.each do |dependency|
78
+ collect_dependencies_versions(dependency, metadata) unless metadata.include? dependency
79
+ end
80
+ return metadata
81
+ end
82
+
83
+ def get_module_buffer(author, name, version)
84
+ @repo.get_module(author, name, version) or raise ModuleNotFound
85
+ end
86
+
87
+ def retrieve_metadata(author, module_name)
88
+ @repo.get_metadata(author, module_name).map {|metadata| ModuleMetadata.new(metadata)}
89
+ end
90
+
91
+ def retrieve_all_metadata
92
+ @repo.get_all_metadata.map {|metadata| ModuleMetadata.new(metadata)}
93
+ end
94
+ end
95
+
96
+ class Search
97
+ def initialize(query)
98
+ @query = query
99
+ end
100
+
101
+ def matches?(metadata)
102
+ return true if @query.nil?
103
+ return true if metadata.name.include? @query
104
+ return true if metadata.author.include? @query
105
+ return false
106
+ end
107
+ end
108
+
109
+ class ModuleMetadata
110
+ def initialize(metadata)
111
+ @metadata = metadata
112
+ end
113
+
114
+ def author
115
+ @metadata["name"][/^[^-]+/]
116
+ end
117
+
118
+ def name
119
+ @metadata["name"].sub(/^[^-]+-/, "")
120
+ end
121
+
122
+ def full_name
123
+ @metadata["name"].sub("-", "/")
124
+ end
125
+
126
+ def version
127
+ @metadata["version"]
128
+ end
129
+
130
+ def dependencies
131
+ @metadata["dependencies"]
132
+ end
133
+
134
+ def summary
135
+ @metadata["summary"]
136
+ end
137
+
138
+ def description
139
+ @metadata["description"]
140
+ end
141
+
142
+ def project_page
143
+ @metadata["project_page"]
144
+ end
145
+
146
+ def dependency_names
147
+ dependencies.map {|d| d["name"]}
148
+ end
149
+
150
+ def to_info
151
+ {
152
+ "author" => author,
153
+ "full_name" => full_name,
154
+ "name" => name,
155
+ "desc" => description,
156
+ "releases" => [ { "version" => version } ]
157
+ }
158
+ end
159
+
160
+ def to_version
161
+ {
162
+ "file" => "/modules/#{author}-#{name}-#{version}.tar.gz",
163
+ "version" => version,
164
+ "dependencies" => dependencies.map do |dependency|
165
+ [ dependency["name"], dependency["version_requirement"] ]
166
+ end
167
+ }
168
+ end
169
+
170
+ def to_search_result
171
+ {
172
+ "author" => author,
173
+ "full_name" => full_name,
174
+ "name" => name,
175
+ "desc" => summary,
176
+ "project_url" => project_page,
177
+ "releases" => [{ "version" => version}],
178
+ "version" => version,
179
+ "tag_list" => [author, name]
180
+ }
181
+ end
182
+ end
183
+ end
@@ -17,10 +17,12 @@
17
17
  require 'json'
18
18
  require 'rubygems/package'
19
19
  require 'zlib'
20
+ require 'puppet_library/forge/abstract'
20
21
 
21
- module PuppetLibrary::ModuleRepo
22
- class Directory
22
+ module PuppetLibrary::Forge
23
+ class Directory < PuppetLibrary::Forge::Abstract
23
24
  def initialize(module_dir)
25
+ super(self)
24
26
  @module_dir = module_dir
25
27
  end
26
28
 
@@ -34,7 +36,7 @@ module PuppetLibrary::ModuleRepo
34
36
  end
35
37
  end
36
38
 
37
- def get_metadata(author, module_name)
39
+ def get_metadata(author = "*", module_name = "")
38
40
  Dir["#{@module_dir}/#{author}-#{module_name}*"].map do |module_path|
39
41
  tar = Gem::Package::TarReader.new(Zlib::GzipReader.open(module_path))
40
42
  tar.rewind
@@ -42,5 +44,9 @@ module PuppetLibrary::ModuleRepo
42
44
  JSON.parse(metadata_file.read)
43
45
  end
44
46
  end
47
+
48
+ def get_all_metadata
49
+ get_metadata
50
+ end
45
51
  end
46
52
  end
@@ -0,0 +1,81 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # Puppet Library
3
+ # Copyright (C) 2014 drrb
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'puppet_library/forge/search_result'
19
+
20
+ module PuppetLibrary::Forge
21
+ class Multi
22
+ def add_forge(forge)
23
+ forges << forge
24
+ end
25
+
26
+ def search_modules(query)
27
+ all_results = forges.map do |forge|
28
+ forge.search_modules(query)
29
+ end.flatten
30
+
31
+ SearchResult.merge_by_full_name(all_results)
32
+ end
33
+
34
+ def get_module_buffer(author, name, version)
35
+ forges.each do |forge|
36
+ begin
37
+ return forge.get_module_buffer(author, name, version)
38
+ rescue ModuleNotFound
39
+ # Try the next one
40
+ end
41
+ end
42
+ raise ModuleNotFound
43
+ end
44
+
45
+ def get_module_metadata(author, name)
46
+ metadata_list = forges.inject([]) do |metadata_list, forge|
47
+ begin
48
+ metadata_list << forge.get_module_metadata(author, name)
49
+ rescue ModuleNotFound
50
+ metadata_list
51
+ end
52
+ end
53
+ raise ModuleNotFound if metadata_list.empty?
54
+ metadata_list.deep_merge.tap do |metadata|
55
+ metadata["releases"] = metadata["releases"].unique_by { |release| release["version"] }
56
+ end
57
+ end
58
+
59
+ def get_module_metadata_with_dependencies(author, name, version)
60
+ metadata_list = []
61
+ forges.each do |forge|
62
+ begin
63
+ metadata_list << forge.get_module_metadata_with_dependencies(author, name, version)
64
+ rescue ModuleNotFound
65
+ # Try the next one
66
+ end
67
+ end
68
+ raise ModuleNotFound if metadata_list.empty?
69
+ metadata_list.deep_merge.tap do |metadata|
70
+ metadata.each do |module_name, releases|
71
+ metadata[module_name] = releases.unique_by { |release| release["version"] }
72
+ end
73
+ end
74
+ end
75
+
76
+ private
77
+ def forges
78
+ @forges ||= []
79
+ end
80
+ end
81
+ end
@@ -20,8 +20,7 @@ require 'puppet_library/http/cache/in_memory'
20
20
  require 'puppet_library/http/cache/noop'
21
21
  require 'puppet_library/http/url'
22
22
 
23
- module PuppetLibrary::ModuleRepo
24
- #include PuppetLibrary::Http
23
+ module PuppetLibrary::Forge
25
24
  class Proxy
26
25
  def initialize(url,
27
26
  query_cache = PuppetLibrary::Http::Cache::InMemory.new,
@@ -33,29 +32,37 @@ module PuppetLibrary::ModuleRepo
33
32
  @download_cache = download_cache
34
33
  end
35
34
 
36
- def get_module(author, name, version)
37
- version_info = get_module_version(author, name, version)
38
- if version_info.nil?
39
- nil
40
- else
35
+ def search_modules(query)
36
+ query_parameter = query.nil? ? "" : "?q=#{query}"
37
+ results = get("/modules.json#{query_parameter}")
38
+ JSON.parse results
39
+ end
40
+
41
+ def get_module_buffer(author, name, version)
42
+ begin
43
+ version_info = get_module_version(author, name, version)
44
+ raise ModuleNotFound if version_info.nil?
41
45
  download_file(version_info["file"])
46
+ rescue OpenURI::HTTPError
47
+ raise ModuleNotFound
42
48
  end
43
49
  end
44
50
 
45
- def get_metadata(author, name)
46
- module_versions = get_module_versions(author, name)
47
- module_versions.map do |version_info|
48
- {
49
- "name" => "#{author}-#{name}",
50
- "author" => author,
51
- "version" => version_info["version"],
52
- "dependencies" => version_info["dependencies"].map do |(dep_name, dep_spec)|
53
- { "name" => dep_name, "version_requirement" => dep_spec }
54
- end
55
- }
51
+ def get_module_metadata(author, name)
52
+ begin
53
+ response = get("/#{author}/#{name}.json")
54
+ JSON.parse(response)
55
+ rescue OpenURI::HTTPError
56
+ raise ModuleNotFound
57
+ end
58
+ end
59
+
60
+ def get_module_metadata_with_dependencies(author, name, version)
61
+ begin
62
+ look_up_releases(author, name, version)
63
+ rescue OpenURI::HTTPError
64
+ raise ModuleNotFound
56
65
  end
57
- rescue OpenURI::HTTPError => http_error
58
- return []
59
66
  end
60
67
 
61
68
  private
@@ -71,8 +78,10 @@ module PuppetLibrary::ModuleRepo
71
78
  versions["#{author}/#{name}"]
72
79
  end
73
80
 
74
- def look_up_releases(author, name)
75
- response = get("/api/v1/releases.json?module=#{author}/#{name}")
81
+ def look_up_releases(author, name, version = nil)
82
+ version_query = version ? "&version=#{version}" : ""
83
+ url = "/api/v1/releases.json?module=#{author}/#{name}#{version_query}"
84
+ response = get(url)
76
85
  JSON.parse(response)
77
86
  end
78
87
 
@@ -93,3 +102,4 @@ module PuppetLibrary::ModuleRepo
93
102
  end
94
103
  end
95
104
  end
105
+
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # Puppet Library
3
+ # Copyright (C) 2014 drrb
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'puppet_library/util'
19
+
20
+ module PuppetLibrary::Forge::SearchResult
21
+ def self.merge_by_full_name(results)
22
+ results_by_module = results.group_by do |result|
23
+ result["full_name"]
24
+ end
25
+
26
+ results_by_module.values.map do |module_results|
27
+ combine_search_results(module_results)
28
+ end.flatten
29
+ end
30
+
31
+ def self.combine_search_results(search_results)
32
+ highest_version, tags, releases = search_results.inject([nil, [], []]) do |(highest_version, tags, releases), result|
33
+ [
34
+ max_version(highest_version, result["version"]),
35
+ tags + (result["tag_list"] || []),
36
+ releases + (result["releases"] || [])
37
+ ]
38
+ end
39
+
40
+ combined_result = search_results.first.tap do |result|
41
+ result["version"] = highest_version
42
+ result["tag_list"] = tags.uniq
43
+ result["releases"] = releases.uniq.version_sort_by {|r| r["version"]}.reverse
44
+ end
45
+ end
46
+
47
+ def self.max_version(left, right)
48
+ [Gem::Version.new(left), Gem::Version.new(right)].max.version
49
+ end
50
+ end